roojs-all.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                );
705 /*
706  * Based on:
707  * Ext JS Library 1.1.1
708  * Copyright(c) 2006-2007, Ext JS, LLC.
709  *
710  * Originally Released Under LGPL - original licence link has changed is not relivant.
711  *
712  * Fork - LGPL
713  * <script type="text/javascript">
714  */
715
716 (function() {    
717     // wrappedn so fnCleanup is not in global scope...
718     if(Roo.isIE) {
719         function fnCleanUp() {
720             var p = Function.prototype;
721             delete p.createSequence;
722             delete p.defer;
723             delete p.createDelegate;
724             delete p.createCallback;
725             delete p.createInterceptor;
726
727             window.detachEvent("onunload", fnCleanUp);
728         }
729         window.attachEvent("onunload", fnCleanUp);
730     }
731 })();
732
733
734 /**
735  * @class Function
736  * These functions are available on every Function object (any JavaScript function).
737  */
738 Roo.apply(Function.prototype, {
739      /**
740      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
741      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
742      * Will create a function that is bound to those 2 args.
743      * @return {Function} The new function
744     */
745     createCallback : function(/*args...*/){
746         // make args available, in function below
747         var args = arguments;
748         var method = this;
749         return function() {
750             return method.apply(window, args);
751         };
752     },
753
754     /**
755      * Creates a delegate (callback) that sets the scope to obj.
756      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
757      * Will create a function that is automatically scoped to this.
758      * @param {Object} obj (optional) The object for which the scope is set
759      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
760      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
761      *                                             if a number the args are inserted at the specified position
762      * @return {Function} The new function
763      */
764     createDelegate : function(obj, args, appendArgs){
765         var method = this;
766         return function() {
767             var callArgs = args || arguments;
768             if(appendArgs === true){
769                 callArgs = Array.prototype.slice.call(arguments, 0);
770                 callArgs = callArgs.concat(args);
771             }else if(typeof appendArgs == "number"){
772                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
773                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
774                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
775             }
776             return method.apply(obj || window, callArgs);
777         };
778     },
779
780     /**
781      * Calls this function after the number of millseconds specified.
782      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
783      * @param {Object} obj (optional) The object for which the scope is set
784      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
785      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
786      *                                             if a number the args are inserted at the specified position
787      * @return {Number} The timeout id that can be used with clearTimeout
788      */
789     defer : function(millis, obj, args, appendArgs){
790         var fn = this.createDelegate(obj, args, appendArgs);
791         if(millis){
792             return setTimeout(fn, millis);
793         }
794         fn();
795         return 0;
796     },
797     /**
798      * Create a combined function call sequence of the original function + the passed function.
799      * The resulting function returns the results of the original function.
800      * The passed fcn is called with the parameters of the original function
801      * @param {Function} fcn The function to sequence
802      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
803      * @return {Function} The new function
804      */
805     createSequence : function(fcn, scope){
806         if(typeof fcn != "function"){
807             return this;
808         }
809         var method = this;
810         return function() {
811             var retval = method.apply(this || window, arguments);
812             fcn.apply(scope || this || window, arguments);
813             return retval;
814         };
815     },
816
817     /**
818      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
819      * The resulting function returns the results of the original function.
820      * The passed fcn is called with the parameters of the original function.
821      * @addon
822      * @param {Function} fcn The function to call before the original
823      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
824      * @return {Function} The new function
825      */
826     createInterceptor : function(fcn, scope){
827         if(typeof fcn != "function"){
828             return this;
829         }
830         var method = this;
831         return function() {
832             fcn.target = this;
833             fcn.method = method;
834             if(fcn.apply(scope || this || window, arguments) === false){
835                 return;
836             }
837             return method.apply(this || window, arguments);
838         };
839     }
840 });
841 /*
842  * Based on:
843  * Ext JS Library 1.1.1
844  * Copyright(c) 2006-2007, Ext JS, LLC.
845  *
846  * Originally Released Under LGPL - original licence link has changed is not relivant.
847  *
848  * Fork - LGPL
849  * <script type="text/javascript">
850  */
851
852 Roo.applyIf(String, {
853     
854     /** @scope String */
855     
856     /**
857      * Escapes the passed string for ' and \
858      * @param {String} string The string to escape
859      * @return {String} The escaped string
860      * @static
861      */
862     escape : function(string) {
863         return string.replace(/('|\\)/g, "\\$1");
864     },
865
866     /**
867      * Pads the left side of a string with a specified character.  This is especially useful
868      * for normalizing number and date strings.  Example usage:
869      * <pre><code>
870 var s = String.leftPad('123', 5, '0');
871 // s now contains the string: '00123'
872 </code></pre>
873      * @param {String} string The original string
874      * @param {Number} size The total length of the output string
875      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
876      * @return {String} The padded string
877      * @static
878      */
879     leftPad : function (val, size, ch) {
880         var result = new String(val);
881         if(ch === null || ch === undefined || ch === '') {
882             ch = " ";
883         }
884         while (result.length < size) {
885             result = ch + result;
886         }
887         return result;
888     },
889
890     /**
891      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
892      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
893      * <pre><code>
894 var cls = 'my-class', text = 'Some text';
895 var s = String.format('<div class="{0}">{1}</div>', cls, text);
896 // s now contains the string: '<div class="my-class">Some text</div>'
897 </code></pre>
898      * @param {String} string The tokenized string to be formatted
899      * @param {String} value1 The value to replace token {0}
900      * @param {String} value2 Etc...
901      * @return {String} The formatted string
902      * @static
903      */
904     format : function(format){
905         var args = Array.prototype.slice.call(arguments, 1);
906         return format.replace(/\{(\d+)\}/g, function(m, i){
907             return Roo.util.Format.htmlEncode(args[i]);
908         });
909     }
910   
911     
912 });
913
914 /**
915  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
916  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
917  * they are already different, the first value passed in is returned.  Note that this method returns the new value
918  * but does not change the current string.
919  * <pre><code>
920 // alternate sort directions
921 sort = sort.toggle('ASC', 'DESC');
922
923 // instead of conditional logic:
924 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
925 </code></pre>
926  * @param {String} value The value to compare to the current string
927  * @param {String} other The new value to use if the string already equals the first value passed in
928  * @return {String} The new value
929  */
930  
931 String.prototype.toggle = function(value, other){
932     return this == value ? other : value;
933 };
934
935
936 /**
937   * Remove invalid unicode characters from a string 
938   *
939   * @return {String} The clean string
940   */
941 String.prototype.unicodeClean = function () {
942     return this.replace(/[\s\S]/g,
943         function(character) {
944             if (character.charCodeAt()< 256) {
945               return character;
946            }
947            try {
948                 encodeURIComponent(character);
949            } catch(e) { 
950               return '';
951            }
952            return character;
953         }
954     );
955 };
956   
957
958 /**
959   * Make the first letter of a string uppercase
960   *
961   * @return {String} The new string.
962   */
963 String.prototype.toUpperCaseFirst = function () {
964     return this.charAt(0).toUpperCase() + this.slice(1);
965 };  
966   
967 /*
968  * Based on:
969  * Ext JS Library 1.1.1
970  * Copyright(c) 2006-2007, Ext JS, LLC.
971  *
972  * Originally Released Under LGPL - original licence link has changed is not relivant.
973  *
974  * Fork - LGPL
975  * <script type="text/javascript">
976  */
977
978  /**
979  * @class Number
980  */
981 Roo.applyIf(Number.prototype, {
982     /**
983      * Checks whether or not the current number is within a desired range.  If the number is already within the
984      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
985      * exceeded.  Note that this method returns the constrained value but does not change the current number.
986      * @param {Number} min The minimum number in the range
987      * @param {Number} max The maximum number in the range
988      * @return {Number} The constrained value if outside the range, otherwise the current value
989      */
990     constrain : function(min, max){
991         return Math.min(Math.max(this, min), max);
992     }
993 });/*
994  * Based on:
995  * Ext JS Library 1.1.1
996  * Copyright(c) 2006-2007, Ext JS, LLC.
997  *
998  * Originally Released Under LGPL - original licence link has changed is not relivant.
999  *
1000  * Fork - LGPL
1001  * <script type="text/javascript">
1002  */
1003  /**
1004  * @class Array
1005  */
1006 Roo.applyIf(Array.prototype, {
1007     /**
1008      * 
1009      * Checks whether or not the specified object exists in the array.
1010      * @param {Object} o The object to check for
1011      * @return {Number} The index of o in the array (or -1 if it is not found)
1012      */
1013     indexOf : function(o){
1014        for (var i = 0, len = this.length; i < len; i++){
1015               if(this[i] == o) { return i; }
1016        }
1017            return -1;
1018     },
1019
1020     /**
1021      * Removes the specified object from the array.  If the object is not found nothing happens.
1022      * @param {Object} o The object to remove
1023      */
1024     remove : function(o){
1025        var index = this.indexOf(o);
1026        if(index != -1){
1027            this.splice(index, 1);
1028        }
1029     },
1030     /**
1031      * Map (JS 1.6 compatibility)
1032      * @param {Function} function  to call
1033      */
1034     map : function(fun )
1035     {
1036         var len = this.length >>> 0;
1037         if (typeof fun != "function") {
1038             throw new TypeError();
1039         }
1040         var res = new Array(len);
1041         var thisp = arguments[1];
1042         for (var i = 0; i < len; i++)
1043         {
1044             if (i in this) {
1045                 res[i] = fun.call(thisp, this[i], i, this);
1046             }
1047         }
1048
1049         return res;
1050     },
1051     /**
1052      * equals
1053      * @param {Array} o The array to compare to
1054      * @returns {Boolean} true if the same
1055      */
1056     equals : function(b)
1057     {
1058             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1059         if (this === b) {
1060             return true;
1061         }
1062         if (b == null) {
1063             return false;
1064         }
1065         if (this.length !== b.length) {
1066             return false;
1067         }
1068           
1069         // sort?? a.sort().equals(b.sort());
1070           
1071         for (var i = 0; i < this.length; ++i) {
1072             if (this[i] !== b[i]) {
1073             return false;
1074             }
1075         }
1076         return true;
1077     } 
1078     
1079     
1080     
1081     
1082 });
1083
1084 Roo.applyIf(Array, {
1085  /**
1086      * from
1087      * @static
1088      * @param {Array} o Or Array like object (eg. nodelist)
1089      * @returns {Array} 
1090      */
1091     from : function(o)
1092     {
1093         var ret= [];
1094     
1095         for (var i =0; i < o.length; i++) { 
1096             ret[i] = o[i];
1097         }
1098         return ret;
1099       
1100     }
1101 });
1102 /*
1103  * Based on:
1104  * Ext JS Library 1.1.1
1105  * Copyright(c) 2006-2007, Ext JS, LLC.
1106  *
1107  * Originally Released Under LGPL - original licence link has changed is not relivant.
1108  *
1109  * Fork - LGPL
1110  * <script type="text/javascript">
1111  */
1112
1113 /**
1114  * @class Date
1115  *
1116  * The date parsing and format syntax is a subset of
1117  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1118  * supported will provide results equivalent to their PHP versions.
1119  *
1120  * Following is the list of all currently supported formats:
1121  *<pre>
1122 Sample date:
1123 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1124
1125 Format  Output      Description
1126 ------  ----------  --------------------------------------------------------------
1127   d      10         Day of the month, 2 digits with leading zeros
1128   D      Wed        A textual representation of a day, three letters
1129   j      10         Day of the month without leading zeros
1130   l      Wednesday  A full textual representation of the day of the week
1131   S      th         English ordinal day of month suffix, 2 chars (use with j)
1132   w      3          Numeric representation of the day of the week
1133   z      9          The julian date, or day of the year (0-365)
1134   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1135   F      January    A full textual representation of the month
1136   m      01         Numeric representation of a month, with leading zeros
1137   M      Jan        Month name abbreviation, three letters
1138   n      1          Numeric representation of a month, without leading zeros
1139   t      31         Number of days in the given month
1140   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1141   Y      2007       A full numeric representation of a year, 4 digits
1142   y      07         A two digit representation of a year
1143   a      pm         Lowercase Ante meridiem and Post meridiem
1144   A      PM         Uppercase Ante meridiem and Post meridiem
1145   g      3          12-hour format of an hour without leading zeros
1146   G      15         24-hour format of an hour without leading zeros
1147   h      03         12-hour format of an hour with leading zeros
1148   H      15         24-hour format of an hour with leading zeros
1149   i      05         Minutes with leading zeros
1150   s      01         Seconds, with leading zeros
1151   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1152   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1153   T      CST        Timezone setting of the machine running the code
1154   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1155 </pre>
1156  *
1157  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1158  * <pre><code>
1159 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1160 document.write(dt.format('Y-m-d'));                         //2007-01-10
1161 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1162 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1163  </code></pre>
1164  *
1165  * Here are some standard date/time patterns that you might find helpful.  They
1166  * are not part of the source of Date.js, but to use them you can simply copy this
1167  * block of code into any script that is included after Date.js and they will also become
1168  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1169  * <pre><code>
1170 Date.patterns = {
1171     ISO8601Long:"Y-m-d H:i:s",
1172     ISO8601Short:"Y-m-d",
1173     ShortDate: "n/j/Y",
1174     LongDate: "l, F d, Y",
1175     FullDateTime: "l, F d, Y g:i:s A",
1176     MonthDay: "F d",
1177     ShortTime: "g:i A",
1178     LongTime: "g:i:s A",
1179     SortableDateTime: "Y-m-d\\TH:i:s",
1180     UniversalSortableDateTime: "Y-m-d H:i:sO",
1181     YearMonth: "F, Y"
1182 };
1183 </code></pre>
1184  *
1185  * Example usage:
1186  * <pre><code>
1187 var dt = new Date();
1188 document.write(dt.format(Date.patterns.ShortDate));
1189  </code></pre>
1190  */
1191
1192 /*
1193  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1194  * They generate precompiled functions from date formats instead of parsing and
1195  * processing the pattern every time you format a date.  These functions are available
1196  * on every Date object (any javascript function).
1197  *
1198  * The original article and download are here:
1199  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1200  *
1201  */
1202  
1203  
1204  // was in core
1205 /**
1206  Returns the number of milliseconds between this date and date
1207  @param {Date} date (optional) Defaults to now
1208  @return {Number} The diff in milliseconds
1209  @member Date getElapsed
1210  */
1211 Date.prototype.getElapsed = function(date) {
1212         return Math.abs((date || new Date()).getTime()-this.getTime());
1213 };
1214 // was in date file..
1215
1216
1217 // private
1218 Date.parseFunctions = {count:0};
1219 // private
1220 Date.parseRegexes = [];
1221 // private
1222 Date.formatFunctions = {count:0};
1223
1224 // private
1225 Date.prototype.dateFormat = function(format) {
1226     if (Date.formatFunctions[format] == null) {
1227         Date.createNewFormat(format);
1228     }
1229     var func = Date.formatFunctions[format];
1230     return this[func]();
1231 };
1232
1233
1234 /**
1235  * Formats a date given the supplied format string
1236  * @param {String} format The format string
1237  * @return {String} The formatted date
1238  * @method
1239  */
1240 Date.prototype.format = Date.prototype.dateFormat;
1241
1242 // private
1243 Date.createNewFormat = function(format) {
1244     var funcName = "format" + Date.formatFunctions.count++;
1245     Date.formatFunctions[format] = funcName;
1246     var code = "Date.prototype." + funcName + " = function(){return ";
1247     var special = false;
1248     var ch = '';
1249     for (var i = 0; i < format.length; ++i) {
1250         ch = format.charAt(i);
1251         if (!special && ch == "\\") {
1252             special = true;
1253         }
1254         else if (special) {
1255             special = false;
1256             code += "'" + String.escape(ch) + "' + ";
1257         }
1258         else {
1259             code += Date.getFormatCode(ch);
1260         }
1261     }
1262     /** eval:var:zzzzzzzzzzzzz */
1263     eval(code.substring(0, code.length - 3) + ";}");
1264 };
1265
1266 // private
1267 Date.getFormatCode = function(character) {
1268     switch (character) {
1269     case "d":
1270         return "String.leftPad(this.getDate(), 2, '0') + ";
1271     case "D":
1272         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1273     case "j":
1274         return "this.getDate() + ";
1275     case "l":
1276         return "Date.dayNames[this.getDay()] + ";
1277     case "S":
1278         return "this.getSuffix() + ";
1279     case "w":
1280         return "this.getDay() + ";
1281     case "z":
1282         return "this.getDayOfYear() + ";
1283     case "W":
1284         return "this.getWeekOfYear() + ";
1285     case "F":
1286         return "Date.monthNames[this.getMonth()] + ";
1287     case "m":
1288         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1289     case "M":
1290         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1291     case "n":
1292         return "(this.getMonth() + 1) + ";
1293     case "t":
1294         return "this.getDaysInMonth() + ";
1295     case "L":
1296         return "(this.isLeapYear() ? 1 : 0) + ";
1297     case "Y":
1298         return "this.getFullYear() + ";
1299     case "y":
1300         return "('' + this.getFullYear()).substring(2, 4) + ";
1301     case "a":
1302         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1303     case "A":
1304         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1305     case "g":
1306         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1307     case "G":
1308         return "this.getHours() + ";
1309     case "h":
1310         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1311     case "H":
1312         return "String.leftPad(this.getHours(), 2, '0') + ";
1313     case "i":
1314         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1315     case "s":
1316         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1317     case "O":
1318         return "this.getGMTOffset() + ";
1319     case "P":
1320         return "this.getGMTColonOffset() + ";
1321     case "T":
1322         return "this.getTimezone() + ";
1323     case "Z":
1324         return "(this.getTimezoneOffset() * -60) + ";
1325     default:
1326         return "'" + String.escape(character) + "' + ";
1327     }
1328 };
1329
1330 /**
1331  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1332  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1333  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1334  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1335  * string or the parse operation will fail.
1336  * Example Usage:
1337 <pre><code>
1338 //dt = Fri May 25 2007 (current date)
1339 var dt = new Date();
1340
1341 //dt = Thu May 25 2006 (today's month/day in 2006)
1342 dt = Date.parseDate("2006", "Y");
1343
1344 //dt = Sun Jan 15 2006 (all date parts specified)
1345 dt = Date.parseDate("2006-1-15", "Y-m-d");
1346
1347 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1348 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1349 </code></pre>
1350  * @param {String} input The unparsed date as a string
1351  * @param {String} format The format the date is in
1352  * @return {Date} The parsed date
1353  * @static
1354  */
1355 Date.parseDate = function(input, format) {
1356     if (Date.parseFunctions[format] == null) {
1357         Date.createParser(format);
1358     }
1359     var func = Date.parseFunctions[format];
1360     return Date[func](input);
1361 };
1362 /**
1363  * @private
1364  */
1365
1366 Date.createParser = function(format) {
1367     var funcName = "parse" + Date.parseFunctions.count++;
1368     var regexNum = Date.parseRegexes.length;
1369     var currentGroup = 1;
1370     Date.parseFunctions[format] = funcName;
1371
1372     var code = "Date." + funcName + " = function(input){\n"
1373         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1374         + "var d = new Date();\n"
1375         + "y = d.getFullYear();\n"
1376         + "m = d.getMonth();\n"
1377         + "d = d.getDate();\n"
1378         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1379         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1380         + "if (results && results.length > 0) {";
1381     var regex = "";
1382
1383     var special = false;
1384     var ch = '';
1385     for (var i = 0; i < format.length; ++i) {
1386         ch = format.charAt(i);
1387         if (!special && ch == "\\") {
1388             special = true;
1389         }
1390         else if (special) {
1391             special = false;
1392             regex += String.escape(ch);
1393         }
1394         else {
1395             var obj = Date.formatCodeToRegex(ch, currentGroup);
1396             currentGroup += obj.g;
1397             regex += obj.s;
1398             if (obj.g && obj.c) {
1399                 code += obj.c;
1400             }
1401         }
1402     }
1403
1404     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1405         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1406         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1407         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1408         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1409         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1410         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1411         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1412         + "else if (y >= 0 && m >= 0)\n"
1413         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1414         + "else if (y >= 0)\n"
1415         + "{v = new Date(y); v.setFullYear(y);}\n"
1416         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1417         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1418         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1419         + ";}";
1420
1421     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1422     /** eval:var:zzzzzzzzzzzzz */
1423     eval(code);
1424 };
1425
1426 // private
1427 Date.formatCodeToRegex = function(character, currentGroup) {
1428     switch (character) {
1429     case "D":
1430         return {g:0,
1431         c:null,
1432         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1433     case "j":
1434         return {g:1,
1435             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1436             s:"(\\d{1,2})"}; // day of month without leading zeroes
1437     case "d":
1438         return {g:1,
1439             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1440             s:"(\\d{2})"}; // day of month with leading zeroes
1441     case "l":
1442         return {g:0,
1443             c:null,
1444             s:"(?:" + Date.dayNames.join("|") + ")"};
1445     case "S":
1446         return {g:0,
1447             c:null,
1448             s:"(?:st|nd|rd|th)"};
1449     case "w":
1450         return {g:0,
1451             c:null,
1452             s:"\\d"};
1453     case "z":
1454         return {g:0,
1455             c:null,
1456             s:"(?:\\d{1,3})"};
1457     case "W":
1458         return {g:0,
1459             c:null,
1460             s:"(?:\\d{2})"};
1461     case "F":
1462         return {g:1,
1463             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1464             s:"(" + Date.monthNames.join("|") + ")"};
1465     case "M":
1466         return {g:1,
1467             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1468             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1469     case "n":
1470         return {g:1,
1471             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1472             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1473     case "m":
1474         return {g:1,
1475             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1476             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1477     case "t":
1478         return {g:0,
1479             c:null,
1480             s:"\\d{1,2}"};
1481     case "L":
1482         return {g:0,
1483             c:null,
1484             s:"(?:1|0)"};
1485     case "Y":
1486         return {g:1,
1487             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1488             s:"(\\d{4})"};
1489     case "y":
1490         return {g:1,
1491             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1492                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1493             s:"(\\d{1,2})"};
1494     case "a":
1495         return {g:1,
1496             c:"if (results[" + currentGroup + "] == 'am') {\n"
1497                 + "if (h == 12) { h = 0; }\n"
1498                 + "} else { if (h < 12) { h += 12; }}",
1499             s:"(am|pm)"};
1500     case "A":
1501         return {g:1,
1502             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1503                 + "if (h == 12) { h = 0; }\n"
1504                 + "} else { if (h < 12) { h += 12; }}",
1505             s:"(AM|PM)"};
1506     case "g":
1507     case "G":
1508         return {g:1,
1509             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1511     case "h":
1512     case "H":
1513         return {g:1,
1514             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1515             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1516     case "i":
1517         return {g:1,
1518             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1519             s:"(\\d{2})"};
1520     case "s":
1521         return {g:1,
1522             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1523             s:"(\\d{2})"};
1524     case "O":
1525         return {g:1,
1526             c:[
1527                 "o = results[", currentGroup, "];\n",
1528                 "var sn = o.substring(0,1);\n", // get + / - sign
1529                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1530                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1531                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1532                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1533             ].join(""),
1534             s:"([+\-]\\d{2,4})"};
1535     
1536     
1537     case "P":
1538         return {g:1,
1539                 c:[
1540                    "o = results[", currentGroup, "];\n",
1541                    "var sn = o.substring(0,1);\n",
1542                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1543                    "var mn = o.substring(4,6) % 60;\n",
1544                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1545                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1546             ].join(""),
1547             s:"([+\-]\\d{4})"};
1548     case "T":
1549         return {g:0,
1550             c:null,
1551             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1552     case "Z":
1553         return {g:1,
1554             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1555                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1556             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1557     default:
1558         return {g:0,
1559             c:null,
1560             s:String.escape(character)};
1561     }
1562 };
1563
1564 /**
1565  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1566  * @return {String} The abbreviated timezone name (e.g. 'CST')
1567  */
1568 Date.prototype.getTimezone = function() {
1569     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1574  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1575  */
1576 Date.prototype.getGMTOffset = function() {
1577     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1578         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1579         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1580 };
1581
1582 /**
1583  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1584  * @return {String} 2-characters representing hours and 2-characters representing minutes
1585  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1586  */
1587 Date.prototype.getGMTColonOffset = function() {
1588         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1589                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1590                 + ":"
1591                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1592 }
1593
1594 /**
1595  * Get the numeric day number of the year, adjusted for leap year.
1596  * @return {Number} 0 through 364 (365 in leap years)
1597  */
1598 Date.prototype.getDayOfYear = function() {
1599     var num = 0;
1600     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1601     for (var i = 0; i < this.getMonth(); ++i) {
1602         num += Date.daysInMonth[i];
1603     }
1604     return num + this.getDate() - 1;
1605 };
1606
1607 /**
1608  * Get the string representation of the numeric week number of the year
1609  * (equivalent to the format specifier 'W').
1610  * @return {String} '00' through '52'
1611  */
1612 Date.prototype.getWeekOfYear = function() {
1613     // Skip to Thursday of this week
1614     var now = this.getDayOfYear() + (4 - this.getDay());
1615     // Find the first Thursday of the year
1616     var jan1 = new Date(this.getFullYear(), 0, 1);
1617     var then = (7 - jan1.getDay() + 4);
1618     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1619 };
1620
1621 /**
1622  * Whether or not the current date is in a leap year.
1623  * @return {Boolean} True if the current date is in a leap year, else false
1624  */
1625 Date.prototype.isLeapYear = function() {
1626     var year = this.getFullYear();
1627     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1628 };
1629
1630 /**
1631  * Get the first day of the current month, adjusted for leap year.  The returned value
1632  * is the numeric day index within the week (0-6) which can be used in conjunction with
1633  * the {@link #monthNames} array to retrieve the textual day name.
1634  * Example:
1635  *<pre><code>
1636 var dt = new Date('1/10/2007');
1637 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1638 </code></pre>
1639  * @return {Number} The day number (0-6)
1640  */
1641 Date.prototype.getFirstDayOfMonth = function() {
1642     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1643     return (day < 0) ? (day + 7) : day;
1644 };
1645
1646 /**
1647  * Get the last day of the current month, adjusted for leap year.  The returned value
1648  * is the numeric day index within the week (0-6) which can be used in conjunction with
1649  * the {@link #monthNames} array to retrieve the textual day name.
1650  * Example:
1651  *<pre><code>
1652 var dt = new Date('1/10/2007');
1653 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1654 </code></pre>
1655  * @return {Number} The day number (0-6)
1656  */
1657 Date.prototype.getLastDayOfMonth = function() {
1658     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1659     return (day < 0) ? (day + 7) : day;
1660 };
1661
1662
1663 /**
1664  * Get the first date of this date's month
1665  * @return {Date}
1666  */
1667 Date.prototype.getFirstDateOfMonth = function() {
1668     return new Date(this.getFullYear(), this.getMonth(), 1);
1669 };
1670
1671 /**
1672  * Get the last date of this date's month
1673  * @return {Date}
1674  */
1675 Date.prototype.getLastDateOfMonth = function() {
1676     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1677 };
1678 /**
1679  * Get the number of days in the current month, adjusted for leap year.
1680  * @return {Number} The number of days in the month
1681  */
1682 Date.prototype.getDaysInMonth = function() {
1683     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1684     return Date.daysInMonth[this.getMonth()];
1685 };
1686
1687 /**
1688  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1689  * @return {String} 'st, 'nd', 'rd' or 'th'
1690  */
1691 Date.prototype.getSuffix = function() {
1692     switch (this.getDate()) {
1693         case 1:
1694         case 21:
1695         case 31:
1696             return "st";
1697         case 2:
1698         case 22:
1699             return "nd";
1700         case 3:
1701         case 23:
1702             return "rd";
1703         default:
1704             return "th";
1705     }
1706 };
1707
1708 // private
1709 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1710
1711 /**
1712  * An array of textual month names.
1713  * Override these values for international dates, for example...
1714  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1715  * @type Array
1716  * @static
1717  */
1718 Date.monthNames =
1719    ["January",
1720     "February",
1721     "March",
1722     "April",
1723     "May",
1724     "June",
1725     "July",
1726     "August",
1727     "September",
1728     "October",
1729     "November",
1730     "December"];
1731
1732 /**
1733  * An array of textual day names.
1734  * Override these values for international dates, for example...
1735  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1736  * @type Array
1737  * @static
1738  */
1739 Date.dayNames =
1740    ["Sunday",
1741     "Monday",
1742     "Tuesday",
1743     "Wednesday",
1744     "Thursday",
1745     "Friday",
1746     "Saturday"];
1747
1748 // private
1749 Date.y2kYear = 50;
1750 // private
1751 Date.monthNumbers = {
1752     Jan:0,
1753     Feb:1,
1754     Mar:2,
1755     Apr:3,
1756     May:4,
1757     Jun:5,
1758     Jul:6,
1759     Aug:7,
1760     Sep:8,
1761     Oct:9,
1762     Nov:10,
1763     Dec:11};
1764
1765 /**
1766  * Creates and returns a new Date instance with the exact same date value as the called instance.
1767  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1768  * variable will also be changed.  When the intention is to create a new variable that will not
1769  * modify the original instance, you should create a clone.
1770  *
1771  * Example of correctly cloning a date:
1772  * <pre><code>
1773 //wrong way:
1774 var orig = new Date('10/1/2006');
1775 var copy = orig;
1776 copy.setDate(5);
1777 document.write(orig);  //returns 'Thu Oct 05 2006'!
1778
1779 //correct way:
1780 var orig = new Date('10/1/2006');
1781 var copy = orig.clone();
1782 copy.setDate(5);
1783 document.write(orig);  //returns 'Thu Oct 01 2006'
1784 </code></pre>
1785  * @return {Date} The new Date instance
1786  */
1787 Date.prototype.clone = function() {
1788         return new Date(this.getTime());
1789 };
1790
1791 /**
1792  * Clears any time information from this date
1793  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1794  @return {Date} this or the clone
1795  */
1796 Date.prototype.clearTime = function(clone){
1797     if(clone){
1798         return this.clone().clearTime();
1799     }
1800     this.setHours(0);
1801     this.setMinutes(0);
1802     this.setSeconds(0);
1803     this.setMilliseconds(0);
1804     return this;
1805 };
1806
1807 // private
1808 // safari setMonth is broken -- check that this is only donw once...
1809 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1810     Date.brokenSetMonth = Date.prototype.setMonth;
1811         Date.prototype.setMonth = function(num){
1812                 if(num <= -1){
1813                         var n = Math.ceil(-num);
1814                         var back_year = Math.ceil(n/12);
1815                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1816                         this.setFullYear(this.getFullYear() - back_year);
1817                         return Date.brokenSetMonth.call(this, month);
1818                 } else {
1819                         return Date.brokenSetMonth.apply(this, arguments);
1820                 }
1821         };
1822 }
1823
1824 /** Date interval constant 
1825 * @static 
1826 * @type String */
1827 Date.MILLI = "ms";
1828 /** Date interval constant 
1829 * @static 
1830 * @type String */
1831 Date.SECOND = "s";
1832 /** Date interval constant 
1833 * @static 
1834 * @type String */
1835 Date.MINUTE = "mi";
1836 /** Date interval constant 
1837 * @static 
1838 * @type String */
1839 Date.HOUR = "h";
1840 /** Date interval constant 
1841 * @static 
1842 * @type String */
1843 Date.DAY = "d";
1844 /** Date interval constant 
1845 * @static 
1846 * @type String */
1847 Date.MONTH = "mo";
1848 /** Date interval constant 
1849 * @static 
1850 * @type String */
1851 Date.YEAR = "y";
1852
1853 /**
1854  * Provides a convenient method of performing basic date arithmetic.  This method
1855  * does not modify the Date instance being called - it creates and returns
1856  * a new Date instance containing the resulting date value.
1857  *
1858  * Examples:
1859  * <pre><code>
1860 //Basic usage:
1861 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1862 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1863
1864 //Negative values will subtract correctly:
1865 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1866 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1867
1868 //You can even chain several calls together in one line!
1869 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1870 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1871  </code></pre>
1872  *
1873  * @param {String} interval   A valid date interval enum value
1874  * @param {Number} value      The amount to add to the current date
1875  * @return {Date} The new Date instance
1876  */
1877 Date.prototype.add = function(interval, value){
1878   var d = this.clone();
1879   if (!interval || value === 0) { return d; }
1880   switch(interval.toLowerCase()){
1881     case Date.MILLI:
1882       d.setMilliseconds(this.getMilliseconds() + value);
1883       break;
1884     case Date.SECOND:
1885       d.setSeconds(this.getSeconds() + value);
1886       break;
1887     case Date.MINUTE:
1888       d.setMinutes(this.getMinutes() + value);
1889       break;
1890     case Date.HOUR:
1891       d.setHours(this.getHours() + value);
1892       break;
1893     case Date.DAY:
1894       d.setDate(this.getDate() + value);
1895       break;
1896     case Date.MONTH:
1897       var day = this.getDate();
1898       if(day > 28){
1899           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1900       }
1901       d.setDate(day);
1902       d.setMonth(this.getMonth() + value);
1903       break;
1904     case Date.YEAR:
1905       d.setFullYear(this.getFullYear() + value);
1906       break;
1907   }
1908   return d;
1909 };
1910 /**
1911  * @class Roo.lib.Dom
1912  * @licence LGPL
1913  * @static
1914  * 
1915  * Dom utils (from YIU afaik)
1916  *
1917  * 
1918  **/
1919 Roo.lib.Dom = {
1920     /**
1921      * Get the view width
1922      * @param {Boolean} full True will get the full document, otherwise it's the view width
1923      * @return {Number} The width
1924      */
1925      
1926     getViewWidth : function(full) {
1927         return full ? this.getDocumentWidth() : this.getViewportWidth();
1928     },
1929     /**
1930      * Get the view height
1931      * @param {Boolean} full True will get the full document, otherwise it's the view height
1932      * @return {Number} The height
1933      */
1934     getViewHeight : function(full) {
1935         return full ? this.getDocumentHeight() : this.getViewportHeight();
1936     },
1937     /**
1938      * Get the Full Document height 
1939      * @return {Number} The height
1940      */
1941     getDocumentHeight: function() {
1942         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1943         return Math.max(scrollHeight, this.getViewportHeight());
1944     },
1945     /**
1946      * Get the Full Document width
1947      * @return {Number} The width
1948      */
1949     getDocumentWidth: function() {
1950         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1951         return Math.max(scrollWidth, this.getViewportWidth());
1952     },
1953     /**
1954      * Get the Window Viewport height
1955      * @return {Number} The height
1956      */
1957     getViewportHeight: function() {
1958         var height = self.innerHeight;
1959         var mode = document.compatMode;
1960
1961         if ((mode || Roo.isIE) && !Roo.isOpera) {
1962             height = (mode == "CSS1Compat") ?
1963                      document.documentElement.clientHeight :
1964                      document.body.clientHeight;
1965         }
1966
1967         return height;
1968     },
1969     /**
1970      * Get the Window Viewport width
1971      * @return {Number} The width
1972      */
1973     getViewportWidth: function() {
1974         var width = self.innerWidth;
1975         var mode = document.compatMode;
1976
1977         if (mode || Roo.isIE) {
1978             width = (mode == "CSS1Compat") ?
1979                     document.documentElement.clientWidth :
1980                     document.body.clientWidth;
1981         }
1982         return width;
1983     },
1984
1985     isAncestor : function(p, c) {
1986         p = Roo.getDom(p);
1987         c = Roo.getDom(c);
1988         if (!p || !c) {
1989             return false;
1990         }
1991
1992         if (p.contains && !Roo.isSafari) {
1993             return p.contains(c);
1994         } else if (p.compareDocumentPosition) {
1995             return !!(p.compareDocumentPosition(c) & 16);
1996         } else {
1997             var parent = c.parentNode;
1998             while (parent) {
1999                 if (parent == p) {
2000                     return true;
2001                 }
2002                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
2003                     return false;
2004                 }
2005                 parent = parent.parentNode;
2006             }
2007             return false;
2008         }
2009     },
2010
2011     getRegion : function(el) {
2012         return Roo.lib.Region.getRegion(el);
2013     },
2014
2015     getY : function(el) {
2016         return this.getXY(el)[1];
2017     },
2018
2019     getX : function(el) {
2020         return this.getXY(el)[0];
2021     },
2022
2023     getXY : function(el) {
2024         var p, pe, b, scroll, bd = document.body;
2025         el = Roo.getDom(el);
2026         var fly = Roo.lib.AnimBase.fly;
2027         if (el.getBoundingClientRect) {
2028             b = el.getBoundingClientRect();
2029             scroll = fly(document).getScroll();
2030             return [b.left + scroll.left, b.top + scroll.top];
2031         }
2032         var x = 0, y = 0;
2033
2034         p = el;
2035
2036         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2037
2038         while (p) {
2039
2040             x += p.offsetLeft;
2041             y += p.offsetTop;
2042
2043             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2044                 hasAbsolute = true;
2045             }
2046
2047             if (Roo.isGecko) {
2048                 pe = fly(p);
2049
2050                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2051                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2052
2053
2054                 x += bl;
2055                 y += bt;
2056
2057
2058                 if (p != el && pe.getStyle('overflow') != 'visible') {
2059                     x += bl;
2060                     y += bt;
2061                 }
2062             }
2063             p = p.offsetParent;
2064         }
2065
2066         if (Roo.isSafari && hasAbsolute) {
2067             x -= bd.offsetLeft;
2068             y -= bd.offsetTop;
2069         }
2070
2071         if (Roo.isGecko && !hasAbsolute) {
2072             var dbd = fly(bd);
2073             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2074             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2075         }
2076
2077         p = el.parentNode;
2078         while (p && p != bd) {
2079             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2080                 x -= p.scrollLeft;
2081                 y -= p.scrollTop;
2082             }
2083             p = p.parentNode;
2084         }
2085         return [x, y];
2086     },
2087  
2088   
2089
2090
2091     setXY : function(el, xy) {
2092         el = Roo.fly(el, '_setXY');
2093         el.position();
2094         var pts = el.translatePoints(xy);
2095         if (xy[0] !== false) {
2096             el.dom.style.left = pts.left + "px";
2097         }
2098         if (xy[1] !== false) {
2099             el.dom.style.top = pts.top + "px";
2100         }
2101     },
2102
2103     setX : function(el, x) {
2104         this.setXY(el, [x, false]);
2105     },
2106
2107     setY : function(el, y) {
2108         this.setXY(el, [false, y]);
2109     }
2110 };
2111 /*
2112  * Portions of this file are based on pieces of Yahoo User Interface Library
2113  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2114  * YUI licensed under the BSD License:
2115  * http://developer.yahoo.net/yui/license.txt
2116  * <script type="text/javascript">
2117  *
2118  */
2119
2120 Roo.lib.Event = function() {
2121     var loadComplete = false;
2122     var listeners = [];
2123     var unloadListeners = [];
2124     var retryCount = 0;
2125     var onAvailStack = [];
2126     var counter = 0;
2127     var lastError = null;
2128
2129     return {
2130         POLL_RETRYS: 200,
2131         POLL_INTERVAL: 20,
2132         EL: 0,
2133         TYPE: 1,
2134         FN: 2,
2135         WFN: 3,
2136         OBJ: 3,
2137         ADJ_SCOPE: 4,
2138         _interval: null,
2139
2140         startInterval: function() {
2141             if (!this._interval) {
2142                 var self = this;
2143                 var callback = function() {
2144                     self._tryPreloadAttach();
2145                 };
2146                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2147
2148             }
2149         },
2150
2151         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2152             onAvailStack.push({ id:         p_id,
2153                 fn:         p_fn,
2154                 obj:        p_obj,
2155                 override:   p_override,
2156                 checkReady: false    });
2157
2158             retryCount = this.POLL_RETRYS;
2159             this.startInterval();
2160         },
2161
2162
2163         addListener: function(el, eventName, fn) {
2164             el = Roo.getDom(el);
2165             if (!el || !fn) {
2166                 return false;
2167             }
2168
2169             if ("unload" == eventName) {
2170                 unloadListeners[unloadListeners.length] =
2171                 [el, eventName, fn];
2172                 return true;
2173             }
2174
2175             var wrappedFn = function(e) {
2176                 return fn(Roo.lib.Event.getEvent(e));
2177             };
2178
2179             var li = [el, eventName, fn, wrappedFn];
2180
2181             var index = listeners.length;
2182             listeners[index] = li;
2183
2184             this.doAdd(el, eventName, wrappedFn, false);
2185             return true;
2186
2187         },
2188
2189
2190         removeListener: function(el, eventName, fn) {
2191             var i, len;
2192
2193             el = Roo.getDom(el);
2194
2195             if(!fn) {
2196                 return this.purgeElement(el, false, eventName);
2197             }
2198
2199
2200             if ("unload" == eventName) {
2201
2202                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2203                     var li = unloadListeners[i];
2204                     if (li &&
2205                         li[0] == el &&
2206                         li[1] == eventName &&
2207                         li[2] == fn) {
2208                         unloadListeners.splice(i, 1);
2209                         return true;
2210                     }
2211                 }
2212
2213                 return false;
2214             }
2215
2216             var cacheItem = null;
2217
2218
2219             var index = arguments[3];
2220
2221             if ("undefined" == typeof index) {
2222                 index = this._getCacheIndex(el, eventName, fn);
2223             }
2224
2225             if (index >= 0) {
2226                 cacheItem = listeners[index];
2227             }
2228
2229             if (!el || !cacheItem) {
2230                 return false;
2231             }
2232
2233             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2234
2235             delete listeners[index][this.WFN];
2236             delete listeners[index][this.FN];
2237             listeners.splice(index, 1);
2238
2239             return true;
2240
2241         },
2242
2243
2244         getTarget: function(ev, resolveTextNode) {
2245             ev = ev.browserEvent || ev;
2246             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2247             var t = ev.target || ev.srcElement;
2248             return this.resolveTextNode(t);
2249         },
2250
2251
2252         resolveTextNode: function(node) {
2253             if (Roo.isSafari && node && 3 == node.nodeType) {
2254                 return node.parentNode;
2255             } else {
2256                 return node;
2257             }
2258         },
2259
2260
2261         getPageX: function(ev) {
2262             ev = ev.browserEvent || ev;
2263             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2264             var x = ev.pageX;
2265             if (!x && 0 !== x) {
2266                 x = ev.clientX || 0;
2267
2268                 if (Roo.isIE) {
2269                     x += this.getScroll()[1];
2270                 }
2271             }
2272
2273             return x;
2274         },
2275
2276
2277         getPageY: function(ev) {
2278             ev = ev.browserEvent || ev;
2279             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2280             var y = ev.pageY;
2281             if (!y && 0 !== y) {
2282                 y = ev.clientY || 0;
2283
2284                 if (Roo.isIE) {
2285                     y += this.getScroll()[0];
2286                 }
2287             }
2288
2289
2290             return y;
2291         },
2292
2293
2294         getXY: function(ev) {
2295             ev = ev.browserEvent || ev;
2296             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2297             return [this.getPageX(ev), this.getPageY(ev)];
2298         },
2299
2300
2301         getRelatedTarget: function(ev) {
2302             ev = ev.browserEvent || ev;
2303             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2304             var t = ev.relatedTarget;
2305             if (!t) {
2306                 if (ev.type == "mouseout") {
2307                     t = ev.toElement;
2308                 } else if (ev.type == "mouseover") {
2309                     t = ev.fromElement;
2310                 }
2311             }
2312
2313             return this.resolveTextNode(t);
2314         },
2315
2316
2317         getTime: function(ev) {
2318             ev = ev.browserEvent || ev;
2319             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2320             if (!ev.time) {
2321                 var t = new Date().getTime();
2322                 try {
2323                     ev.time = t;
2324                 } catch(ex) {
2325                     this.lastError = ex;
2326                     return t;
2327                 }
2328             }
2329
2330             return ev.time;
2331         },
2332
2333
2334         stopEvent: function(ev) {
2335             this.stopPropagation(ev);
2336             this.preventDefault(ev);
2337         },
2338
2339
2340         stopPropagation: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if (ev.stopPropagation) {
2343                 ev.stopPropagation();
2344             } else {
2345                 ev.cancelBubble = true;
2346             }
2347         },
2348
2349
2350         preventDefault: function(ev) {
2351             ev = ev.browserEvent || ev;
2352             if(ev.preventDefault) {
2353                 ev.preventDefault();
2354             } else {
2355                 ev.returnValue = false;
2356             }
2357         },
2358
2359
2360         getEvent: function(e) {
2361             var ev = e || window.event;
2362             if (!ev) {
2363                 var c = this.getEvent.caller;
2364                 while (c) {
2365                     ev = c.arguments[0];
2366                     if (ev && Event == ev.constructor) {
2367                         break;
2368                     }
2369                     c = c.caller;
2370                 }
2371             }
2372             return ev;
2373         },
2374
2375
2376         getCharCode: function(ev) {
2377             ev = ev.browserEvent || ev;
2378             return ev.charCode || ev.keyCode || 0;
2379         },
2380
2381
2382         _getCacheIndex: function(el, eventName, fn) {
2383             for (var i = 0,len = listeners.length; i < len; ++i) {
2384                 var li = listeners[i];
2385                 if (li &&
2386                     li[this.FN] == fn &&
2387                     li[this.EL] == el &&
2388                     li[this.TYPE] == eventName) {
2389                     return i;
2390                 }
2391             }
2392
2393             return -1;
2394         },
2395
2396
2397         elCache: {},
2398
2399
2400         getEl: function(id) {
2401             return document.getElementById(id);
2402         },
2403
2404
2405         clearCache: function() {
2406         },
2407
2408
2409         _load: function(e) {
2410             loadComplete = true;
2411             var EU = Roo.lib.Event;
2412
2413
2414             if (Roo.isIE) {
2415                 EU.doRemove(window, "load", EU._load);
2416             }
2417         },
2418
2419
2420         _tryPreloadAttach: function() {
2421
2422             if (this.locked) {
2423                 return false;
2424             }
2425
2426             this.locked = true;
2427
2428
2429             var tryAgain = !loadComplete;
2430             if (!tryAgain) {
2431                 tryAgain = (retryCount > 0);
2432             }
2433
2434
2435             var notAvail = [];
2436             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2437                 var item = onAvailStack[i];
2438                 if (item) {
2439                     var el = this.getEl(item.id);
2440
2441                     if (el) {
2442                         if (!item.checkReady ||
2443                             loadComplete ||
2444                             el.nextSibling ||
2445                             (document && document.body)) {
2446
2447                             var scope = el;
2448                             if (item.override) {
2449                                 if (item.override === true) {
2450                                     scope = item.obj;
2451                                 } else {
2452                                     scope = item.override;
2453                                 }
2454                             }
2455                             item.fn.call(scope, item.obj);
2456                             onAvailStack[i] = null;
2457                         }
2458                     } else {
2459                         notAvail.push(item);
2460                     }
2461                 }
2462             }
2463
2464             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2465
2466             if (tryAgain) {
2467
2468                 this.startInterval();
2469             } else {
2470                 clearInterval(this._interval);
2471                 this._interval = null;
2472             }
2473
2474             this.locked = false;
2475
2476             return true;
2477
2478         },
2479
2480
2481         purgeElement: function(el, recurse, eventName) {
2482             var elListeners = this.getListeners(el, eventName);
2483             if (elListeners) {
2484                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2485                     var l = elListeners[i];
2486                     this.removeListener(el, l.type, l.fn);
2487                 }
2488             }
2489
2490             if (recurse && el && el.childNodes) {
2491                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2492                     this.purgeElement(el.childNodes[i], recurse, eventName);
2493                 }
2494             }
2495         },
2496
2497
2498         getListeners: function(el, eventName) {
2499             var results = [], searchLists;
2500             if (!eventName) {
2501                 searchLists = [listeners, unloadListeners];
2502             } else if (eventName == "unload") {
2503                 searchLists = [unloadListeners];
2504             } else {
2505                 searchLists = [listeners];
2506             }
2507
2508             for (var j = 0; j < searchLists.length; ++j) {
2509                 var searchList = searchLists[j];
2510                 if (searchList && searchList.length > 0) {
2511                     for (var i = 0,len = searchList.length; i < len; ++i) {
2512                         var l = searchList[i];
2513                         if (l && l[this.EL] === el &&
2514                             (!eventName || eventName === l[this.TYPE])) {
2515                             results.push({
2516                                 type:   l[this.TYPE],
2517                                 fn:     l[this.FN],
2518                                 obj:    l[this.OBJ],
2519                                 adjust: l[this.ADJ_SCOPE],
2520                                 index:  i
2521                             });
2522                         }
2523                     }
2524                 }
2525             }
2526
2527             return (results.length) ? results : null;
2528         },
2529
2530
2531         _unload: function(e) {
2532
2533             var EU = Roo.lib.Event, i, j, l, len, index;
2534
2535             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2536                 l = unloadListeners[i];
2537                 if (l) {
2538                     var scope = window;
2539                     if (l[EU.ADJ_SCOPE]) {
2540                         if (l[EU.ADJ_SCOPE] === true) {
2541                             scope = l[EU.OBJ];
2542                         } else {
2543                             scope = l[EU.ADJ_SCOPE];
2544                         }
2545                     }
2546                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2547                     unloadListeners[i] = null;
2548                     l = null;
2549                     scope = null;
2550                 }
2551             }
2552
2553             unloadListeners = null;
2554
2555             if (listeners && listeners.length > 0) {
2556                 j = listeners.length;
2557                 while (j) {
2558                     index = j - 1;
2559                     l = listeners[index];
2560                     if (l) {
2561                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2562                                 l[EU.FN], index);
2563                     }
2564                     j = j - 1;
2565                 }
2566                 l = null;
2567
2568                 EU.clearCache();
2569             }
2570
2571             EU.doRemove(window, "unload", EU._unload);
2572
2573         },
2574
2575
2576         getScroll: function() {
2577             var dd = document.documentElement, db = document.body;
2578             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2579                 return [dd.scrollTop, dd.scrollLeft];
2580             } else if (db) {
2581                 return [db.scrollTop, db.scrollLeft];
2582             } else {
2583                 return [0, 0];
2584             }
2585         },
2586
2587
2588         doAdd: function () {
2589             if (window.addEventListener) {
2590                 return function(el, eventName, fn, capture) {
2591                     el.addEventListener(eventName, fn, (capture));
2592                 };
2593             } else if (window.attachEvent) {
2594                 return function(el, eventName, fn, capture) {
2595                     el.attachEvent("on" + eventName, fn);
2596                 };
2597             } else {
2598                 return function() {
2599                 };
2600             }
2601         }(),
2602
2603
2604         doRemove: function() {
2605             if (window.removeEventListener) {
2606                 return function (el, eventName, fn, capture) {
2607                     el.removeEventListener(eventName, fn, (capture));
2608                 };
2609             } else if (window.detachEvent) {
2610                 return function (el, eventName, fn) {
2611                     el.detachEvent("on" + eventName, fn);
2612                 };
2613             } else {
2614                 return function() {
2615                 };
2616             }
2617         }()
2618     };
2619     
2620 }();
2621 (function() {     
2622    
2623     var E = Roo.lib.Event;
2624     E.on = E.addListener;
2625     E.un = E.removeListener;
2626
2627     if (document && document.body) {
2628         E._load();
2629     } else {
2630         E.doAdd(window, "load", E._load);
2631     }
2632     E.doAdd(window, "unload", E._unload);
2633     E._tryPreloadAttach();
2634 })();
2635
2636  
2637
2638 (function() {
2639     /**
2640      * @class Roo.lib.Ajax
2641      *
2642      * provide a simple Ajax request utility functions
2643      * 
2644      * Portions of this file are based on pieces of Yahoo User Interface Library
2645     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2646     * YUI licensed under the BSD License:
2647     * http://developer.yahoo.net/yui/license.txt
2648     * <script type="text/javascript">
2649     *
2650      *
2651      */
2652     Roo.lib.Ajax = {
2653         /**
2654          * @static 
2655          */
2656         request : function(method, uri, cb, data, options) {
2657             if(options){
2658                 var hs = options.headers;
2659                 if(hs){
2660                     for(var h in hs){
2661                         if(hs.hasOwnProperty(h)){
2662                             this.initHeader(h, hs[h], false);
2663                         }
2664                     }
2665                 }
2666                 if(options.xmlData){
2667                     this.initHeader('Content-Type', 'text/xml', false);
2668                     method = 'POST';
2669                     data = options.xmlData;
2670                 }
2671             }
2672
2673             return this.asyncRequest(method, uri, cb, data);
2674         },
2675         /**
2676          * serialize a form
2677          *
2678          * @static
2679          * @param {DomForm} form element
2680          * @return {String} urlencode form output.
2681          */
2682         serializeForm : function(form) {
2683             if(typeof form == 'string') {
2684                 form = (document.getElementById(form) || document.forms[form]);
2685             }
2686
2687             var el, name, val, disabled, data = '', hasSubmit = false;
2688             for (var i = 0; i < form.elements.length; i++) {
2689                 el = form.elements[i];
2690                 disabled = form.elements[i].disabled;
2691                 name = form.elements[i].name;
2692                 val = form.elements[i].value;
2693
2694                 if (!disabled && name){
2695                     switch (el.type)
2696                             {
2697                         case 'select-one':
2698                         case 'select-multiple':
2699                             for (var j = 0; j < el.options.length; j++) {
2700                                 if (el.options[j].selected) {
2701                                     if (Roo.isIE) {
2702                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2703                                     }
2704                                     else {
2705                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2706                                     }
2707                                 }
2708                             }
2709                             break;
2710                         case 'radio':
2711                         case 'checkbox':
2712                             if (el.checked) {
2713                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2714                             }
2715                             break;
2716                         case 'file':
2717
2718                         case undefined:
2719
2720                         case 'reset':
2721
2722                         case 'button':
2723
2724                             break;
2725                         case 'submit':
2726                             if(hasSubmit == false) {
2727                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2728                                 hasSubmit = true;
2729                             }
2730                             break;
2731                         default:
2732                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2733                             break;
2734                     }
2735                 }
2736             }
2737             data = data.substr(0, data.length - 1);
2738             return data;
2739         },
2740
2741         headers:{},
2742
2743         hasHeaders:false,
2744
2745         useDefaultHeader:true,
2746
2747         defaultPostHeader:'application/x-www-form-urlencoded',
2748
2749         useDefaultXhrHeader:true,
2750
2751         defaultXhrHeader:'XMLHttpRequest',
2752
2753         hasDefaultHeaders:true,
2754
2755         defaultHeaders:{},
2756
2757         poll:{},
2758
2759         timeout:{},
2760
2761         pollInterval:50,
2762
2763         transactionId:0,
2764
2765         setProgId:function(id)
2766         {
2767             this.activeX.unshift(id);
2768         },
2769
2770         setDefaultPostHeader:function(b)
2771         {
2772             this.useDefaultHeader = b;
2773         },
2774
2775         setDefaultXhrHeader:function(b)
2776         {
2777             this.useDefaultXhrHeader = b;
2778         },
2779
2780         setPollingInterval:function(i)
2781         {
2782             if (typeof i == 'number' && isFinite(i)) {
2783                 this.pollInterval = i;
2784             }
2785         },
2786
2787         createXhrObject:function(transactionId)
2788         {
2789             var obj,http;
2790             try
2791             {
2792
2793                 http = new XMLHttpRequest();
2794
2795                 obj = { conn:http, tId:transactionId };
2796             }
2797             catch(e)
2798             {
2799                 for (var i = 0; i < this.activeX.length; ++i) {
2800                     try
2801                     {
2802
2803                         http = new ActiveXObject(this.activeX[i]);
2804
2805                         obj = { conn:http, tId:transactionId };
2806                         break;
2807                     }
2808                     catch(e) {
2809                     }
2810                 }
2811             }
2812             finally
2813             {
2814                 return obj;
2815             }
2816         },
2817
2818         getConnectionObject:function()
2819         {
2820             var o;
2821             var tId = this.transactionId;
2822
2823             try
2824             {
2825                 o = this.createXhrObject(tId);
2826                 if (o) {
2827                     this.transactionId++;
2828                 }
2829             }
2830             catch(e) {
2831             }
2832             finally
2833             {
2834                 return o;
2835             }
2836         },
2837
2838         asyncRequest:function(method, uri, callback, postData)
2839         {
2840             var o = this.getConnectionObject();
2841
2842             if (!o) {
2843                 return null;
2844             }
2845             else {
2846                 o.conn.open(method, uri, true);
2847
2848                 if (this.useDefaultXhrHeader) {
2849                     if (!this.defaultHeaders['X-Requested-With']) {
2850                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2851                     }
2852                 }
2853
2854                 if(postData && this.useDefaultHeader){
2855                     this.initHeader('Content-Type', this.defaultPostHeader);
2856                 }
2857
2858                  if (this.hasDefaultHeaders || this.hasHeaders) {
2859                     this.setHeader(o);
2860                 }
2861
2862                 this.handleReadyState(o, callback);
2863                 o.conn.send(postData || null);
2864
2865                 return o;
2866             }
2867         },
2868
2869         handleReadyState:function(o, callback)
2870         {
2871             var oConn = this;
2872
2873             if (callback && callback.timeout) {
2874                 
2875                 this.timeout[o.tId] = window.setTimeout(function() {
2876                     oConn.abort(o, callback, true);
2877                 }, callback.timeout);
2878             }
2879
2880             this.poll[o.tId] = window.setInterval(
2881                     function() {
2882                         if (o.conn && o.conn.readyState == 4) {
2883                             window.clearInterval(oConn.poll[o.tId]);
2884                             delete oConn.poll[o.tId];
2885
2886                             if(callback && callback.timeout) {
2887                                 window.clearTimeout(oConn.timeout[o.tId]);
2888                                 delete oConn.timeout[o.tId];
2889                             }
2890
2891                             oConn.handleTransactionResponse(o, callback);
2892                         }
2893                     }
2894                     , this.pollInterval);
2895         },
2896
2897         handleTransactionResponse:function(o, callback, isAbort)
2898         {
2899
2900             if (!callback) {
2901                 this.releaseObject(o);
2902                 return;
2903             }
2904
2905             var httpStatus, responseObject;
2906
2907             try
2908             {
2909                 if (o.conn.status !== undefined && o.conn.status != 0) {
2910                     httpStatus = o.conn.status;
2911                 }
2912                 else {
2913                     httpStatus = 13030;
2914                 }
2915             }
2916             catch(e) {
2917
2918
2919                 httpStatus = 13030;
2920             }
2921
2922             if (httpStatus >= 200 && httpStatus < 300) {
2923                 responseObject = this.createResponseObject(o, callback.argument);
2924                 if (callback.success) {
2925                     if (!callback.scope) {
2926                         callback.success(responseObject);
2927                     }
2928                     else {
2929
2930
2931                         callback.success.apply(callback.scope, [responseObject]);
2932                     }
2933                 }
2934             }
2935             else {
2936                 switch (httpStatus) {
2937
2938                     case 12002:
2939                     case 12029:
2940                     case 12030:
2941                     case 12031:
2942                     case 12152:
2943                     case 13030:
2944                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2945                         if (callback.failure) {
2946                             if (!callback.scope) {
2947                                 callback.failure(responseObject);
2948                             }
2949                             else {
2950                                 callback.failure.apply(callback.scope, [responseObject]);
2951                             }
2952                         }
2953                         break;
2954                     default:
2955                         responseObject = this.createResponseObject(o, callback.argument);
2956                         if (callback.failure) {
2957                             if (!callback.scope) {
2958                                 callback.failure(responseObject);
2959                             }
2960                             else {
2961                                 callback.failure.apply(callback.scope, [responseObject]);
2962                             }
2963                         }
2964                 }
2965             }
2966
2967             this.releaseObject(o);
2968             responseObject = null;
2969         },
2970
2971         createResponseObject:function(o, callbackArg)
2972         {
2973             var obj = {};
2974             var headerObj = {};
2975
2976             try
2977             {
2978                 var headerStr = o.conn.getAllResponseHeaders();
2979                 var header = headerStr.split('\n');
2980                 for (var i = 0; i < header.length; i++) {
2981                     var delimitPos = header[i].indexOf(':');
2982                     if (delimitPos != -1) {
2983                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2984                     }
2985                 }
2986             }
2987             catch(e) {
2988             }
2989
2990             obj.tId = o.tId;
2991             obj.status = o.conn.status;
2992             obj.statusText = o.conn.statusText;
2993             obj.getResponseHeader = headerObj;
2994             obj.getAllResponseHeaders = headerStr;
2995             obj.responseText = o.conn.responseText;
2996             obj.responseXML = o.conn.responseXML;
2997
2998             if (typeof callbackArg !== undefined) {
2999                 obj.argument = callbackArg;
3000             }
3001
3002             return obj;
3003         },
3004
3005         createExceptionObject:function(tId, callbackArg, isAbort)
3006         {
3007             var COMM_CODE = 0;
3008             var COMM_ERROR = 'communication failure';
3009             var ABORT_CODE = -1;
3010             var ABORT_ERROR = 'transaction aborted';
3011
3012             var obj = {};
3013
3014             obj.tId = tId;
3015             if (isAbort) {
3016                 obj.status = ABORT_CODE;
3017                 obj.statusText = ABORT_ERROR;
3018             }
3019             else {
3020                 obj.status = COMM_CODE;
3021                 obj.statusText = COMM_ERROR;
3022             }
3023
3024             if (callbackArg) {
3025                 obj.argument = callbackArg;
3026             }
3027
3028             return obj;
3029         },
3030
3031         initHeader:function(label, value, isDefault)
3032         {
3033             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3034
3035             if (headerObj[label] === undefined) {
3036                 headerObj[label] = value;
3037             }
3038             else {
3039
3040
3041                 headerObj[label] = value + "," + headerObj[label];
3042             }
3043
3044             if (isDefault) {
3045                 this.hasDefaultHeaders = true;
3046             }
3047             else {
3048                 this.hasHeaders = true;
3049             }
3050         },
3051
3052
3053         setHeader:function(o)
3054         {
3055             if (this.hasDefaultHeaders) {
3056                 for (var prop in this.defaultHeaders) {
3057                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3058                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3059                     }
3060                 }
3061             }
3062
3063             if (this.hasHeaders) {
3064                 for (var prop in this.headers) {
3065                     if (this.headers.hasOwnProperty(prop)) {
3066                         o.conn.setRequestHeader(prop, this.headers[prop]);
3067                     }
3068                 }
3069                 this.headers = {};
3070                 this.hasHeaders = false;
3071             }
3072         },
3073
3074         resetDefaultHeaders:function() {
3075             delete this.defaultHeaders;
3076             this.defaultHeaders = {};
3077             this.hasDefaultHeaders = false;
3078         },
3079
3080         abort:function(o, callback, isTimeout)
3081         {
3082             if(this.isCallInProgress(o)) {
3083                 o.conn.abort();
3084                 window.clearInterval(this.poll[o.tId]);
3085                 delete this.poll[o.tId];
3086                 if (isTimeout) {
3087                     delete this.timeout[o.tId];
3088                 }
3089
3090                 this.handleTransactionResponse(o, callback, true);
3091
3092                 return true;
3093             }
3094             else {
3095                 return false;
3096             }
3097         },
3098
3099
3100         isCallInProgress:function(o)
3101         {
3102             if (o && o.conn) {
3103                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3104             }
3105             else {
3106
3107                 return false;
3108             }
3109         },
3110
3111
3112         releaseObject:function(o)
3113         {
3114
3115             o.conn = null;
3116
3117             o = null;
3118         },
3119
3120         activeX:[
3121         'MSXML2.XMLHTTP.3.0',
3122         'MSXML2.XMLHTTP',
3123         'Microsoft.XMLHTTP'
3124         ]
3125
3126
3127     };
3128 })();/*
3129  * Portions of this file are based on pieces of Yahoo User Interface Library
3130  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3131  * YUI licensed under the BSD License:
3132  * http://developer.yahoo.net/yui/license.txt
3133  * <script type="text/javascript">
3134  *
3135  */
3136
3137 Roo.lib.Region = function(t, r, b, l) {
3138     this.top = t;
3139     this[1] = t;
3140     this.right = r;
3141     this.bottom = b;
3142     this.left = l;
3143     this[0] = l;
3144 };
3145
3146
3147 Roo.lib.Region.prototype = {
3148     contains : function(region) {
3149         return ( region.left >= this.left &&
3150                  region.right <= this.right &&
3151                  region.top >= this.top &&
3152                  region.bottom <= this.bottom    );
3153
3154     },
3155
3156     getArea : function() {
3157         return ( (this.bottom - this.top) * (this.right - this.left) );
3158     },
3159
3160     intersect : function(region) {
3161         var t = Math.max(this.top, region.top);
3162         var r = Math.min(this.right, region.right);
3163         var b = Math.min(this.bottom, region.bottom);
3164         var l = Math.max(this.left, region.left);
3165
3166         if (b >= t && r >= l) {
3167             return new Roo.lib.Region(t, r, b, l);
3168         } else {
3169             return null;
3170         }
3171     },
3172     union : function(region) {
3173         var t = Math.min(this.top, region.top);
3174         var r = Math.max(this.right, region.right);
3175         var b = Math.max(this.bottom, region.bottom);
3176         var l = Math.min(this.left, region.left);
3177
3178         return new Roo.lib.Region(t, r, b, l);
3179     },
3180
3181     adjust : function(t, l, b, r) {
3182         this.top += t;
3183         this.left += l;
3184         this.right += r;
3185         this.bottom += b;
3186         return this;
3187     }
3188 };
3189
3190 Roo.lib.Region.getRegion = function(el) {
3191     var p = Roo.lib.Dom.getXY(el);
3192
3193     var t = p[1];
3194     var r = p[0] + el.offsetWidth;
3195     var b = p[1] + el.offsetHeight;
3196     var l = p[0];
3197
3198     return new Roo.lib.Region(t, r, b, l);
3199 };
3200 /*
3201  * Portions of this file are based on pieces of Yahoo User Interface Library
3202  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3203  * YUI licensed under the BSD License:
3204  * http://developer.yahoo.net/yui/license.txt
3205  * <script type="text/javascript">
3206  *
3207  */
3208 //@@dep Roo.lib.Region
3209
3210
3211 Roo.lib.Point = function(x, y) {
3212     if (x instanceof Array) {
3213         y = x[1];
3214         x = x[0];
3215     }
3216     this.x = this.right = this.left = this[0] = x;
3217     this.y = this.top = this.bottom = this[1] = y;
3218 };
3219
3220 Roo.lib.Point.prototype = new Roo.lib.Region();
3221 /*
3222  * Portions of this file are based on pieces of Yahoo User Interface Library
3223  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3224  * YUI licensed under the BSD License:
3225  * http://developer.yahoo.net/yui/license.txt
3226  * <script type="text/javascript">
3227  *
3228  */
3229  
3230 (function() {   
3231
3232     Roo.lib.Anim = {
3233         scroll : function(el, args, duration, easing, cb, scope) {
3234             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3235         },
3236
3237         motion : function(el, args, duration, easing, cb, scope) {
3238             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3239         },
3240
3241         color : function(el, args, duration, easing, cb, scope) {
3242             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3243         },
3244
3245         run : function(el, args, duration, easing, cb, scope, type) {
3246             type = type || Roo.lib.AnimBase;
3247             if (typeof easing == "string") {
3248                 easing = Roo.lib.Easing[easing];
3249             }
3250             var anim = new type(el, args, duration, easing);
3251             anim.animateX(function() {
3252                 Roo.callback(cb, scope);
3253             });
3254             return anim;
3255         }
3256     };
3257 })();/*
3258  * Portions of this file are based on pieces of Yahoo User Interface Library
3259  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3260  * YUI licensed under the BSD License:
3261  * http://developer.yahoo.net/yui/license.txt
3262  * <script type="text/javascript">
3263  *
3264  */
3265
3266 (function() {    
3267     var libFlyweight;
3268     
3269     function fly(el) {
3270         if (!libFlyweight) {
3271             libFlyweight = new Roo.Element.Flyweight();
3272         }
3273         libFlyweight.dom = el;
3274         return libFlyweight;
3275     }
3276
3277     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3278     
3279    
3280     
3281     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3282         if (el) {
3283             this.init(el, attributes, duration, method);
3284         }
3285     };
3286
3287     Roo.lib.AnimBase.fly = fly;
3288     
3289     
3290     
3291     Roo.lib.AnimBase.prototype = {
3292
3293         toString: function() {
3294             var el = this.getEl();
3295             var id = el.id || el.tagName;
3296             return ("Anim " + id);
3297         },
3298
3299         patterns: {
3300             noNegatives:        /width|height|opacity|padding/i,
3301             offsetAttribute:  /^((width|height)|(top|left))$/,
3302             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3303             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3304         },
3305
3306
3307         doMethod: function(attr, start, end) {
3308             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3309         },
3310
3311
3312         setAttribute: function(attr, val, unit) {
3313             if (this.patterns.noNegatives.test(attr)) {
3314                 val = (val > 0) ? val : 0;
3315             }
3316
3317             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3318         },
3319
3320
3321         getAttribute: function(attr) {
3322             var el = this.getEl();
3323             var val = fly(el).getStyle(attr);
3324
3325             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3326                 return parseFloat(val);
3327             }
3328
3329             var a = this.patterns.offsetAttribute.exec(attr) || [];
3330             var pos = !!( a[3] );
3331             var box = !!( a[2] );
3332
3333
3334             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3335                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3336             } else {
3337                 val = 0;
3338             }
3339
3340             return val;
3341         },
3342
3343
3344         getDefaultUnit: function(attr) {
3345             if (this.patterns.defaultUnit.test(attr)) {
3346                 return 'px';
3347             }
3348
3349             return '';
3350         },
3351
3352         animateX : function(callback, scope) {
3353             var f = function() {
3354                 this.onComplete.removeListener(f);
3355                 if (typeof callback == "function") {
3356                     callback.call(scope || this, this);
3357                 }
3358             };
3359             this.onComplete.addListener(f, this);
3360             this.animate();
3361         },
3362
3363
3364         setRuntimeAttribute: function(attr) {
3365             var start;
3366             var end;
3367             var attributes = this.attributes;
3368
3369             this.runtimeAttributes[attr] = {};
3370
3371             var isset = function(prop) {
3372                 return (typeof prop !== 'undefined');
3373             };
3374
3375             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3376                 return false;
3377             }
3378
3379             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3380
3381
3382             if (isset(attributes[attr]['to'])) {
3383                 end = attributes[attr]['to'];
3384             } else if (isset(attributes[attr]['by'])) {
3385                 if (start.constructor == Array) {
3386                     end = [];
3387                     for (var i = 0, len = start.length; i < len; ++i) {
3388                         end[i] = start[i] + attributes[attr]['by'][i];
3389                     }
3390                 } else {
3391                     end = start + attributes[attr]['by'];
3392                 }
3393             }
3394
3395             this.runtimeAttributes[attr].start = start;
3396             this.runtimeAttributes[attr].end = end;
3397
3398
3399             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3400         },
3401
3402
3403         init: function(el, attributes, duration, method) {
3404
3405             var isAnimated = false;
3406
3407
3408             var startTime = null;
3409
3410
3411             var actualFrames = 0;
3412
3413
3414             el = Roo.getDom(el);
3415
3416
3417             this.attributes = attributes || {};
3418
3419
3420             this.duration = duration || 1;
3421
3422
3423             this.method = method || Roo.lib.Easing.easeNone;
3424
3425
3426             this.useSeconds = true;
3427
3428
3429             this.currentFrame = 0;
3430
3431
3432             this.totalFrames = Roo.lib.AnimMgr.fps;
3433
3434
3435             this.getEl = function() {
3436                 return el;
3437             };
3438
3439
3440             this.isAnimated = function() {
3441                 return isAnimated;
3442             };
3443
3444
3445             this.getStartTime = function() {
3446                 return startTime;
3447             };
3448
3449             this.runtimeAttributes = {};
3450
3451
3452             this.animate = function() {
3453                 if (this.isAnimated()) {
3454                     return false;
3455                 }
3456
3457                 this.currentFrame = 0;
3458
3459                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3460
3461                 Roo.lib.AnimMgr.registerElement(this);
3462             };
3463
3464
3465             this.stop = function(finish) {
3466                 if (finish) {
3467                     this.currentFrame = this.totalFrames;
3468                     this._onTween.fire();
3469                 }
3470                 Roo.lib.AnimMgr.stop(this);
3471             };
3472
3473             var onStart = function() {
3474                 this.onStart.fire();
3475
3476                 this.runtimeAttributes = {};
3477                 for (var attr in this.attributes) {
3478                     this.setRuntimeAttribute(attr);
3479                 }
3480
3481                 isAnimated = true;
3482                 actualFrames = 0;
3483                 startTime = new Date();
3484             };
3485
3486
3487             var onTween = function() {
3488                 var data = {
3489                     duration: new Date() - this.getStartTime(),
3490                     currentFrame: this.currentFrame
3491                 };
3492
3493                 data.toString = function() {
3494                     return (
3495                             'duration: ' + data.duration +
3496                             ', currentFrame: ' + data.currentFrame
3497                             );
3498                 };
3499
3500                 this.onTween.fire(data);
3501
3502                 var runtimeAttributes = this.runtimeAttributes;
3503
3504                 for (var attr in runtimeAttributes) {
3505                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3506                 }
3507
3508                 actualFrames += 1;
3509             };
3510
3511             var onComplete = function() {
3512                 var actual_duration = (new Date() - startTime) / 1000 ;
3513
3514                 var data = {
3515                     duration: actual_duration,
3516                     frames: actualFrames,
3517                     fps: actualFrames / actual_duration
3518                 };
3519
3520                 data.toString = function() {
3521                     return (
3522                             'duration: ' + data.duration +
3523                             ', frames: ' + data.frames +
3524                             ', fps: ' + data.fps
3525                             );
3526                 };
3527
3528                 isAnimated = false;
3529                 actualFrames = 0;
3530                 this.onComplete.fire(data);
3531             };
3532
3533
3534             this._onStart = new Roo.util.Event(this);
3535             this.onStart = new Roo.util.Event(this);
3536             this.onTween = new Roo.util.Event(this);
3537             this._onTween = new Roo.util.Event(this);
3538             this.onComplete = new Roo.util.Event(this);
3539             this._onComplete = new Roo.util.Event(this);
3540             this._onStart.addListener(onStart);
3541             this._onTween.addListener(onTween);
3542             this._onComplete.addListener(onComplete);
3543         }
3544     };
3545 })();
3546 /*
3547  * Portions of this file are based on pieces of Yahoo User Interface Library
3548  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3549  * YUI licensed under the BSD License:
3550  * http://developer.yahoo.net/yui/license.txt
3551  * <script type="text/javascript">
3552  *
3553  */
3554
3555 Roo.lib.AnimMgr = new function() {
3556
3557     var thread = null;
3558
3559
3560     var queue = [];
3561
3562
3563     var tweenCount = 0;
3564
3565
3566     this.fps = 1000;
3567
3568
3569     this.delay = 1;
3570
3571
3572     this.registerElement = function(tween) {
3573         queue[queue.length] = tween;
3574         tweenCount += 1;
3575         tween._onStart.fire();
3576         this.start();
3577     };
3578
3579
3580     this.unRegister = function(tween, index) {
3581         tween._onComplete.fire();
3582         index = index || getIndex(tween);
3583         if (index != -1) {
3584             queue.splice(index, 1);
3585         }
3586
3587         tweenCount -= 1;
3588         if (tweenCount <= 0) {
3589             this.stop();
3590         }
3591     };
3592
3593
3594     this.start = function() {
3595         if (thread === null) {
3596             thread = setInterval(this.run, this.delay);
3597         }
3598     };
3599
3600
3601     this.stop = function(tween) {
3602         if (!tween) {
3603             clearInterval(thread);
3604
3605             for (var i = 0, len = queue.length; i < len; ++i) {
3606                 if (queue[0].isAnimated()) {
3607                     this.unRegister(queue[0], 0);
3608                 }
3609             }
3610
3611             queue = [];
3612             thread = null;
3613             tweenCount = 0;
3614         }
3615         else {
3616             this.unRegister(tween);
3617         }
3618     };
3619
3620
3621     this.run = function() {
3622         for (var i = 0, len = queue.length; i < len; ++i) {
3623             var tween = queue[i];
3624             if (!tween || !tween.isAnimated()) {
3625                 continue;
3626             }
3627
3628             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3629             {
3630                 tween.currentFrame += 1;
3631
3632                 if (tween.useSeconds) {
3633                     correctFrame(tween);
3634                 }
3635                 tween._onTween.fire();
3636             }
3637             else {
3638                 Roo.lib.AnimMgr.stop(tween, i);
3639             }
3640         }
3641     };
3642
3643     var getIndex = function(anim) {
3644         for (var i = 0, len = queue.length; i < len; ++i) {
3645             if (queue[i] == anim) {
3646                 return i;
3647             }
3648         }
3649         return -1;
3650     };
3651
3652
3653     var correctFrame = function(tween) {
3654         var frames = tween.totalFrames;
3655         var frame = tween.currentFrame;
3656         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3657         var elapsed = (new Date() - tween.getStartTime());
3658         var tweak = 0;
3659
3660         if (elapsed < tween.duration * 1000) {
3661             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3662         } else {
3663             tweak = frames - (frame + 1);
3664         }
3665         if (tweak > 0 && isFinite(tweak)) {
3666             if (tween.currentFrame + tweak >= frames) {
3667                 tweak = frames - (frame + 1);
3668             }
3669
3670             tween.currentFrame += tweak;
3671         }
3672     };
3673 };
3674
3675     /*
3676  * Portions of this file are based on pieces of Yahoo User Interface Library
3677  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3678  * YUI licensed under the BSD License:
3679  * http://developer.yahoo.net/yui/license.txt
3680  * <script type="text/javascript">
3681  *
3682  */
3683 Roo.lib.Bezier = new function() {
3684
3685         this.getPosition = function(points, t) {
3686             var n = points.length;
3687             var tmp = [];
3688
3689             for (var i = 0; i < n; ++i) {
3690                 tmp[i] = [points[i][0], points[i][1]];
3691             }
3692
3693             for (var j = 1; j < n; ++j) {
3694                 for (i = 0; i < n - j; ++i) {
3695                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3696                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3697                 }
3698             }
3699
3700             return [ tmp[0][0], tmp[0][1] ];
3701
3702         };
3703     }; 
3704
3705 /**
3706  * @class Roo.lib.Color
3707  * @constructor
3708  * An abstract Color implementation. Concrete Color implementations should use
3709  * an instance of this function as their prototype, and implement the getRGB and
3710  * getHSL functions. getRGB should return an object representing the RGB
3711  * components of this Color, with the red, green, and blue components in the
3712  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3713  * return an object representing the HSL components of this Color, with the hue
3714  * component in the range [0,360), the saturation and lightness components in
3715  * the range [0,100], and the alpha component in the range [0,1].
3716  *
3717  *
3718  * Color.js
3719  *
3720  * Functions for Color handling and processing.
3721  *
3722  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3723  *
3724  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3725  * rights to this program, with the intention of it becoming part of the public
3726  * domain. Because this program is released into the public domain, it comes with
3727  * no warranty either expressed or implied, to the extent permitted by law.
3728  * 
3729  * For more free and public domain JavaScript code by the same author, visit:
3730  * http://www.safalra.com/web-design/javascript/
3731  * 
3732  */
3733 Roo.lib.Color = function() { }
3734
3735
3736 Roo.apply(Roo.lib.Color.prototype, {
3737   
3738   rgb : null,
3739   hsv : null,
3740   hsl : null,
3741   
3742   /**
3743    * getIntegerRGB
3744    * @return {Object} an object representing the RGBA components of this Color. The red,
3745    * green, and blue components are converted to integers in the range [0,255].
3746    * The alpha is a value in the range [0,1].
3747    */
3748   getIntegerRGB : function(){
3749
3750     // get the RGB components of this Color
3751     var rgb = this.getRGB();
3752
3753     // return the integer components
3754     return {
3755       'r' : Math.round(rgb.r),
3756       'g' : Math.round(rgb.g),
3757       'b' : Math.round(rgb.b),
3758       'a' : rgb.a
3759     };
3760
3761   },
3762
3763   /**
3764    * getPercentageRGB
3765    * @return {Object} an object representing the RGBA components of this Color. The red,
3766    * green, and blue components are converted to numbers in the range [0,100].
3767    * The alpha is a value in the range [0,1].
3768    */
3769   getPercentageRGB : function(){
3770
3771     // get the RGB components of this Color
3772     var rgb = this.getRGB();
3773
3774     // return the percentage components
3775     return {
3776       'r' : 100 * rgb.r / 255,
3777       'g' : 100 * rgb.g / 255,
3778       'b' : 100 * rgb.b / 255,
3779       'a' : rgb.a
3780     };
3781
3782   },
3783
3784   /**
3785    * getCSSHexadecimalRGB
3786    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3787    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3788    * are two-digit hexadecimal numbers.
3789    */
3790   getCSSHexadecimalRGB : function()
3791   {
3792
3793     // get the integer RGB components
3794     var rgb = this.getIntegerRGB();
3795
3796     // determine the hexadecimal equivalents
3797     var r16 = rgb.r.toString(16);
3798     var g16 = rgb.g.toString(16);
3799     var b16 = rgb.b.toString(16);
3800
3801     // return the CSS RGB Color value
3802     return '#'
3803         + (r16.length == 2 ? r16 : '0' + r16)
3804         + (g16.length == 2 ? g16 : '0' + g16)
3805         + (b16.length == 2 ? b16 : '0' + b16);
3806
3807   },
3808
3809   /**
3810    * getCSSIntegerRGB
3811    * @return {String} a string representing this Color as a CSS integer RGB Color
3812    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3813    * are integers in the range [0,255].
3814    */
3815   getCSSIntegerRGB : function(){
3816
3817     // get the integer RGB components
3818     var rgb = this.getIntegerRGB();
3819
3820     // return the CSS RGB Color value
3821     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3822
3823   },
3824
3825   /**
3826    * getCSSIntegerRGBA
3827    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3828    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3829    * b are integers in the range [0,255] and a is in the range [0,1].
3830    */
3831   getCSSIntegerRGBA : function(){
3832
3833     // get the integer RGB components
3834     var rgb = this.getIntegerRGB();
3835
3836     // return the CSS integer RGBA Color value
3837     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3838
3839   },
3840
3841   /**
3842    * getCSSPercentageRGB
3843    * @return {String} a string representing this Color as a CSS percentage RGB Color
3844    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3845    * b are in the range [0,100].
3846    */
3847   getCSSPercentageRGB : function(){
3848
3849     // get the percentage RGB components
3850     var rgb = this.getPercentageRGB();
3851
3852     // return the CSS RGB Color value
3853     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3854
3855   },
3856
3857   /**
3858    * getCSSPercentageRGBA
3859    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3860    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3861    * and b are in the range [0,100] and a is in the range [0,1].
3862    */
3863   getCSSPercentageRGBA : function(){
3864
3865     // get the percentage RGB components
3866     var rgb = this.getPercentageRGB();
3867
3868     // return the CSS percentage RGBA Color value
3869     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3870
3871   },
3872
3873   /**
3874    * getCSSHSL
3875    * @return {String} a string representing this Color as a CSS HSL Color value - that
3876    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3877    * s and l are in the range [0,100].
3878    */
3879   getCSSHSL : function(){
3880
3881     // get the HSL components
3882     var hsl = this.getHSL();
3883
3884     // return the CSS HSL Color value
3885     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3886
3887   },
3888
3889   /**
3890    * getCSSHSLA
3891    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3892    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3893    * s and l are in the range [0,100], and a is in the range [0,1].
3894    */
3895   getCSSHSLA : function(){
3896
3897     // get the HSL components
3898     var hsl = this.getHSL();
3899
3900     // return the CSS HSL Color value
3901     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3902
3903   },
3904
3905   /**
3906    * Sets the Color of the specified node to this Color. This functions sets
3907    * the CSS 'color' property for the node. The parameter is:
3908    * 
3909    * @param {DomElement} node - the node whose Color should be set
3910    */
3911   setNodeColor : function(node){
3912
3913     // set the Color of the node
3914     node.style.color = this.getCSSHexadecimalRGB();
3915
3916   },
3917
3918   /**
3919    * Sets the background Color of the specified node to this Color. This
3920    * functions sets the CSS 'background-color' property for the node. The
3921    * parameter is:
3922    *
3923    * @param {DomElement} node - the node whose background Color should be set
3924    */
3925   setNodeBackgroundColor : function(node){
3926
3927     // set the background Color of the node
3928     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3929
3930   },
3931   // convert between formats..
3932   toRGB: function()
3933   {
3934     var r = this.getIntegerRGB();
3935     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3936     
3937   },
3938   toHSL : function()
3939   {
3940      var hsl = this.getHSL();
3941   // return the CSS HSL Color value
3942     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3943     
3944   },
3945   
3946   toHSV : function()
3947   {
3948     var rgb = this.toRGB();
3949     var hsv = rgb.getHSV();
3950    // return the CSS HSL Color value
3951     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3952     
3953   },
3954   
3955   // modify  v = 0 ... 1 (eg. 0.5)
3956   saturate : function(v)
3957   {
3958       var rgb = this.toRGB();
3959       var hsv = rgb.getHSV();
3960       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3961       
3962   
3963   },
3964   
3965    
3966   /**
3967    * getRGB
3968    * @return {Object} the RGB and alpha components of this Color as an object with r,
3969    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3970    * the range [0,1].
3971    */
3972   getRGB: function(){
3973    
3974     // return the RGB components
3975     return {
3976       'r' : this.rgb.r,
3977       'g' : this.rgb.g,
3978       'b' : this.rgb.b,
3979       'a' : this.alpha
3980     };
3981
3982   },
3983
3984   /**
3985    * getHSV
3986    * @return {Object} the HSV and alpha components of this Color as an object with h,
3987    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3988    * [0,100], and a is in the range [0,1].
3989    */
3990   getHSV : function()
3991   {
3992     
3993     // calculate the HSV components if necessary
3994     if (this.hsv == null) {
3995       this.calculateHSV();
3996     }
3997
3998     // return the HSV components
3999     return {
4000       'h' : this.hsv.h,
4001       's' : this.hsv.s,
4002       'v' : this.hsv.v,
4003       'a' : this.alpha
4004     };
4005
4006   },
4007
4008   /**
4009    * getHSL
4010    * @return {Object} the HSL and alpha components of this Color as an object with h,
4011    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4012    * [0,100], and a is in the range [0,1].
4013    */
4014   getHSL : function(){
4015     
4016      
4017     // calculate the HSV components if necessary
4018     if (this.hsl == null) { this.calculateHSL(); }
4019
4020     // return the HSL components
4021     return {
4022       'h' : this.hsl.h,
4023       's' : this.hsl.s,
4024       'l' : this.hsl.l,
4025       'a' : this.alpha
4026     };
4027
4028   }
4029   
4030
4031 });
4032
4033
4034 /**
4035  * @class Roo.lib.RGBColor
4036  * @extends Roo.lib.Color
4037  * Creates a Color specified in the RGB Color space, with an optional alpha
4038  * component. The parameters are:
4039  * @constructor
4040  * 
4041
4042  * @param {Number} r - the red component, clipped to the range [0,255]
4043  * @param {Number} g - the green component, clipped to the range [0,255]
4044  * @param {Number} b - the blue component, clipped to the range [0,255]
4045  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4046  *     optional and defaults to 1
4047  */
4048 Roo.lib.RGBColor = function (r, g, b, a){
4049
4050   // store the alpha component after clipping it if necessary
4051   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4052
4053   // store the RGB components after clipping them if necessary
4054   this.rgb =
4055       {
4056         'r' : Math.max(0, Math.min(255, r)),
4057         'g' : Math.max(0, Math.min(255, g)),
4058         'b' : Math.max(0, Math.min(255, b))
4059       };
4060
4061   // initialise the HSV and HSL components to null
4062   
4063
4064   /* 
4065    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4066    * range [0,360). The parameters are:
4067    *
4068    * maximum - the maximum of the RGB component values
4069    * range   - the range of the RGB component values
4070    */
4071    
4072
4073 }
4074 // this does an 'exteds'
4075 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4076
4077   
4078     getHue  : function(maximum, range)
4079     {
4080       var rgb = this.rgb;
4081        
4082       // check whether the range is zero
4083       if (range == 0){
4084   
4085         // set the hue to zero (any hue is acceptable as the Color is grey)
4086         var hue = 0;
4087   
4088       }else{
4089   
4090         // determine which of the components has the highest value and set the hue
4091         switch (maximum){
4092   
4093           // red has the highest value
4094           case rgb.r:
4095             var hue = (rgb.g - rgb.b) / range * 60;
4096             if (hue < 0) { hue += 360; }
4097             break;
4098   
4099           // green has the highest value
4100           case rgb.g:
4101             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4102             break;
4103   
4104           // blue has the highest value
4105           case rgb.b:
4106             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4107             break;
4108   
4109         }
4110   
4111       }
4112   
4113       // return the hue
4114       return hue;
4115   
4116     },
4117
4118   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4119    * be returned be the getHSV function.
4120    */
4121    calculateHSV : function(){
4122     var rgb = this.rgb;
4123     // get the maximum and range of the RGB component values
4124     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4125     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4126
4127     // store the HSV components
4128     this.hsv =
4129         {
4130           'h' : this.getHue(maximum, range),
4131           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4132           'v' : maximum / 2.55
4133         };
4134
4135   },
4136
4137   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4138    * be returned be the getHSL function.
4139    */
4140    calculateHSL : function(){
4141     var rgb = this.rgb;
4142     // get the maximum and range of the RGB component values
4143     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4144     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4145
4146     // determine the lightness in the range [0,1]
4147     var l = maximum / 255 - range / 510;
4148
4149     // store the HSL components
4150     this.hsl =
4151         {
4152           'h' : this.getHue(maximum, range),
4153           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4154           'l' : 100 * l
4155         };
4156
4157   }
4158
4159 });
4160
4161 /**
4162  * @class Roo.lib.HSVColor
4163  * @extends Roo.lib.Color
4164  * Creates a Color specified in the HSV Color space, with an optional alpha
4165  * component. The parameters are:
4166  * @constructor
4167  *
4168  * @param {Number} h - the hue component, wrapped to the range [0,360)
4169  * @param {Number} s - the saturation component, clipped to the range [0,100]
4170  * @param {Number} v - the value component, clipped to the range [0,100]
4171  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4172  *     optional and defaults to 1
4173  */
4174 Roo.lib.HSVColor = function (h, s, v, a){
4175
4176   // store the alpha component after clipping it if necessary
4177   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4178
4179   // store the HSV components after clipping or wrapping them if necessary
4180   this.hsv =
4181       {
4182         'h' : (h % 360 + 360) % 360,
4183         's' : Math.max(0, Math.min(100, s)),
4184         'v' : Math.max(0, Math.min(100, v))
4185       };
4186
4187   // initialise the RGB and HSL components to null
4188   this.rgb = null;
4189   this.hsl = null;
4190 }
4191
4192 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4193   /* Calculates and stores the RGB components of this HSVColor so that they can
4194    * be returned be the getRGB function.
4195    */
4196   calculateRGB: function ()
4197   {
4198     var hsv = this.hsv;
4199     // check whether the saturation is zero
4200     if (hsv.s == 0){
4201
4202       // set the Color to the appropriate shade of grey
4203       var r = hsv.v;
4204       var g = hsv.v;
4205       var b = hsv.v;
4206
4207     }else{
4208
4209       // set some temporary values
4210       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4211       var p  = hsv.v * (1 - hsv.s / 100);
4212       var q  = hsv.v * (1 - hsv.s / 100 * f);
4213       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4214
4215       // set the RGB Color components to their temporary values
4216       switch (Math.floor(hsv.h / 60)){
4217         case 0: var r = hsv.v; var g = t; var b = p; break;
4218         case 1: var r = q; var g = hsv.v; var b = p; break;
4219         case 2: var r = p; var g = hsv.v; var b = t; break;
4220         case 3: var r = p; var g = q; var b = hsv.v; break;
4221         case 4: var r = t; var g = p; var b = hsv.v; break;
4222         case 5: var r = hsv.v; var g = p; var b = q; break;
4223       }
4224
4225     }
4226
4227     // store the RGB components
4228     this.rgb =
4229         {
4230           'r' : r * 2.55,
4231           'g' : g * 2.55,
4232           'b' : b * 2.55
4233         };
4234
4235   },
4236
4237   /* Calculates and stores the HSL components of this HSVColor so that they can
4238    * be returned be the getHSL function.
4239    */
4240   calculateHSL : function (){
4241
4242     var hsv = this.hsv;
4243     // determine the lightness in the range [0,100]
4244     var l = (2 - hsv.s / 100) * hsv.v / 2;
4245
4246     // store the HSL components
4247     this.hsl =
4248         {
4249           'h' : hsv.h,
4250           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4251           'l' : l
4252         };
4253
4254     // correct a division-by-zero error
4255     if (isNaN(hsl.s)) { hsl.s = 0; }
4256
4257   } 
4258  
4259
4260 });
4261  
4262
4263 /**
4264  * @class Roo.lib.HSLColor
4265  * @extends Roo.lib.Color
4266  *
4267  * @constructor
4268  * Creates a Color specified in the HSL Color space, with an optional alpha
4269  * component. The parameters are:
4270  *
4271  * @param {Number} h - the hue component, wrapped to the range [0,360)
4272  * @param {Number} s - the saturation component, clipped to the range [0,100]
4273  * @param {Number} l - the lightness component, clipped to the range [0,100]
4274  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4275  *     optional and defaults to 1
4276  */
4277
4278 Roo.lib.HSLColor = function(h, s, l, a){
4279
4280   // store the alpha component after clipping it if necessary
4281   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4282
4283   // store the HSL components after clipping or wrapping them if necessary
4284   this.hsl =
4285       {
4286         'h' : (h % 360 + 360) % 360,
4287         's' : Math.max(0, Math.min(100, s)),
4288         'l' : Math.max(0, Math.min(100, l))
4289       };
4290
4291   // initialise the RGB and HSV components to null
4292 }
4293
4294 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4295
4296   /* Calculates and stores the RGB components of this HSLColor so that they can
4297    * be returned be the getRGB function.
4298    */
4299   calculateRGB: function (){
4300
4301     // check whether the saturation is zero
4302     if (this.hsl.s == 0){
4303
4304       // store the RGB components representing the appropriate shade of grey
4305       this.rgb =
4306           {
4307             'r' : this.hsl.l * 2.55,
4308             'g' : this.hsl.l * 2.55,
4309             'b' : this.hsl.l * 2.55
4310           };
4311
4312     }else{
4313
4314       // set some temporary values
4315       var p = this.hsl.l < 50
4316             ? this.hsl.l * (1 + hsl.s / 100)
4317             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4318       var q = 2 * hsl.l - p;
4319
4320       // initialise the RGB components
4321       this.rgb =
4322           {
4323             'r' : (h + 120) / 60 % 6,
4324             'g' : h / 60,
4325             'b' : (h + 240) / 60 % 6
4326           };
4327
4328       // loop over the RGB components
4329       for (var key in this.rgb){
4330
4331         // ensure that the property is not inherited from the root object
4332         if (this.rgb.hasOwnProperty(key)){
4333
4334           // set the component to its value in the range [0,100]
4335           if (this.rgb[key] < 1){
4336             this.rgb[key] = q + (p - q) * this.rgb[key];
4337           }else if (this.rgb[key] < 3){
4338             this.rgb[key] = p;
4339           }else if (this.rgb[key] < 4){
4340             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4341           }else{
4342             this.rgb[key] = q;
4343           }
4344
4345           // set the component to its value in the range [0,255]
4346           this.rgb[key] *= 2.55;
4347
4348         }
4349
4350       }
4351
4352     }
4353
4354   },
4355
4356   /* Calculates and stores the HSV components of this HSLColor so that they can
4357    * be returned be the getHSL function.
4358    */
4359    calculateHSV : function(){
4360
4361     // set a temporary value
4362     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4363
4364     // store the HSV components
4365     this.hsv =
4366         {
4367           'h' : this.hsl.h,
4368           's' : 200 * t / (this.hsl.l + t),
4369           'v' : t + this.hsl.l
4370         };
4371
4372     // correct a division-by-zero error
4373     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4374
4375   }
4376  
4377
4378 });
4379 /*
4380  * Portions of this file are based on pieces of Yahoo User Interface Library
4381  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4382  * YUI licensed under the BSD License:
4383  * http://developer.yahoo.net/yui/license.txt
4384  * <script type="text/javascript">
4385  *
4386  */
4387 (function() {
4388
4389     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4390         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4391     };
4392
4393     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4394
4395     var fly = Roo.lib.AnimBase.fly;
4396     var Y = Roo.lib;
4397     var superclass = Y.ColorAnim.superclass;
4398     var proto = Y.ColorAnim.prototype;
4399
4400     proto.toString = function() {
4401         var el = this.getEl();
4402         var id = el.id || el.tagName;
4403         return ("ColorAnim " + id);
4404     };
4405
4406     proto.patterns.color = /color$/i;
4407     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4408     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4409     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4410     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4411
4412
4413     proto.parseColor = function(s) {
4414         if (s.length == 3) {
4415             return s;
4416         }
4417
4418         var c = this.patterns.hex.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4421         }
4422
4423         c = this.patterns.rgb.exec(s);
4424         if (c && c.length == 4) {
4425             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4426         }
4427
4428         c = this.patterns.hex3.exec(s);
4429         if (c && c.length == 4) {
4430             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4431         }
4432
4433         return null;
4434     };
4435     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4436     proto.getAttribute = function(attr) {
4437         var el = this.getEl();
4438         if (this.patterns.color.test(attr)) {
4439             var val = fly(el).getStyle(attr);
4440
4441             if (this.patterns.transparent.test(val)) {
4442                 var parent = el.parentNode;
4443                 val = fly(parent).getStyle(attr);
4444
4445                 while (parent && this.patterns.transparent.test(val)) {
4446                     parent = parent.parentNode;
4447                     val = fly(parent).getStyle(attr);
4448                     if (parent.tagName.toUpperCase() == 'HTML') {
4449                         val = '#fff';
4450                     }
4451                 }
4452             }
4453         } else {
4454             val = superclass.getAttribute.call(this, attr);
4455         }
4456
4457         return val;
4458     };
4459     proto.getAttribute = function(attr) {
4460         var el = this.getEl();
4461         if (this.patterns.color.test(attr)) {
4462             var val = fly(el).getStyle(attr);
4463
4464             if (this.patterns.transparent.test(val)) {
4465                 var parent = el.parentNode;
4466                 val = fly(parent).getStyle(attr);
4467
4468                 while (parent && this.patterns.transparent.test(val)) {
4469                     parent = parent.parentNode;
4470                     val = fly(parent).getStyle(attr);
4471                     if (parent.tagName.toUpperCase() == 'HTML') {
4472                         val = '#fff';
4473                     }
4474                 }
4475             }
4476         } else {
4477             val = superclass.getAttribute.call(this, attr);
4478         }
4479
4480         return val;
4481     };
4482
4483     proto.doMethod = function(attr, start, end) {
4484         var val;
4485
4486         if (this.patterns.color.test(attr)) {
4487             val = [];
4488             for (var i = 0, len = start.length; i < len; ++i) {
4489                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4490             }
4491
4492             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4493         }
4494         else {
4495             val = superclass.doMethod.call(this, attr, start, end);
4496         }
4497
4498         return val;
4499     };
4500
4501     proto.setRuntimeAttribute = function(attr) {
4502         superclass.setRuntimeAttribute.call(this, attr);
4503
4504         if (this.patterns.color.test(attr)) {
4505             var attributes = this.attributes;
4506             var start = this.parseColor(this.runtimeAttributes[attr].start);
4507             var end = this.parseColor(this.runtimeAttributes[attr].end);
4508
4509             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4510                 end = this.parseColor(attributes[attr].by);
4511
4512                 for (var i = 0, len = start.length; i < len; ++i) {
4513                     end[i] = start[i] + end[i];
4514                 }
4515             }
4516
4517             this.runtimeAttributes[attr].start = start;
4518             this.runtimeAttributes[attr].end = end;
4519         }
4520     };
4521 })();
4522
4523 /*
4524  * Portions of this file are based on pieces of Yahoo User Interface Library
4525  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4526  * YUI licensed under the BSD License:
4527  * http://developer.yahoo.net/yui/license.txt
4528  * <script type="text/javascript">
4529  *
4530  */
4531 Roo.lib.Easing = {
4532
4533
4534     easeNone: function (t, b, c, d) {
4535         return c * t / d + b;
4536     },
4537
4538
4539     easeIn: function (t, b, c, d) {
4540         return c * (t /= d) * t + b;
4541     },
4542
4543
4544     easeOut: function (t, b, c, d) {
4545         return -c * (t /= d) * (t - 2) + b;
4546     },
4547
4548
4549     easeBoth: function (t, b, c, d) {
4550         if ((t /= d / 2) < 1) {
4551             return c / 2 * t * t + b;
4552         }
4553
4554         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4555     },
4556
4557
4558     easeInStrong: function (t, b, c, d) {
4559         return c * (t /= d) * t * t * t + b;
4560     },
4561
4562
4563     easeOutStrong: function (t, b, c, d) {
4564         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4565     },
4566
4567
4568     easeBothStrong: function (t, b, c, d) {
4569         if ((t /= d / 2) < 1) {
4570             return c / 2 * t * t * t * t + b;
4571         }
4572
4573         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4574     },
4575
4576
4577
4578     elasticIn: function (t, b, c, d, a, p) {
4579         if (t == 0) {
4580             return b;
4581         }
4582         if ((t /= d) == 1) {
4583             return b + c;
4584         }
4585         if (!p) {
4586             p = d * .3;
4587         }
4588
4589         if (!a || a < Math.abs(c)) {
4590             a = c;
4591             var s = p / 4;
4592         }
4593         else {
4594             var s = p / (2 * Math.PI) * Math.asin(c / a);
4595         }
4596
4597         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4598     },
4599
4600
4601     elasticOut: function (t, b, c, d, a, p) {
4602         if (t == 0) {
4603             return b;
4604         }
4605         if ((t /= d) == 1) {
4606             return b + c;
4607         }
4608         if (!p) {
4609             p = d * .3;
4610         }
4611
4612         if (!a || a < Math.abs(c)) {
4613             a = c;
4614             var s = p / 4;
4615         }
4616         else {
4617             var s = p / (2 * Math.PI) * Math.asin(c / a);
4618         }
4619
4620         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4621     },
4622
4623
4624     elasticBoth: function (t, b, c, d, a, p) {
4625         if (t == 0) {
4626             return b;
4627         }
4628
4629         if ((t /= d / 2) == 2) {
4630             return b + c;
4631         }
4632
4633         if (!p) {
4634             p = d * (.3 * 1.5);
4635         }
4636
4637         if (!a || a < Math.abs(c)) {
4638             a = c;
4639             var s = p / 4;
4640         }
4641         else {
4642             var s = p / (2 * Math.PI) * Math.asin(c / a);
4643         }
4644
4645         if (t < 1) {
4646             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4647                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4648         }
4649         return a * Math.pow(2, -10 * (t -= 1)) *
4650                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4651     },
4652
4653
4654
4655     backIn: function (t, b, c, d, s) {
4656         if (typeof s == 'undefined') {
4657             s = 1.70158;
4658         }
4659         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4660     },
4661
4662
4663     backOut: function (t, b, c, d, s) {
4664         if (typeof s == 'undefined') {
4665             s = 1.70158;
4666         }
4667         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4668     },
4669
4670
4671     backBoth: function (t, b, c, d, s) {
4672         if (typeof s == 'undefined') {
4673             s = 1.70158;
4674         }
4675
4676         if ((t /= d / 2 ) < 1) {
4677             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4678         }
4679         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4680     },
4681
4682
4683     bounceIn: function (t, b, c, d) {
4684         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4685     },
4686
4687
4688     bounceOut: function (t, b, c, d) {
4689         if ((t /= d) < (1 / 2.75)) {
4690             return c * (7.5625 * t * t) + b;
4691         } else if (t < (2 / 2.75)) {
4692             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4693         } else if (t < (2.5 / 2.75)) {
4694             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4695         }
4696         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4697     },
4698
4699
4700     bounceBoth: function (t, b, c, d) {
4701         if (t < d / 2) {
4702             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4703         }
4704         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4705     }
4706 };/*
4707  * Portions of this file are based on pieces of Yahoo User Interface Library
4708  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4709  * YUI licensed under the BSD License:
4710  * http://developer.yahoo.net/yui/license.txt
4711  * <script type="text/javascript">
4712  *
4713  */
4714     (function() {
4715         Roo.lib.Motion = function(el, attributes, duration, method) {
4716             if (el) {
4717                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4718             }
4719         };
4720
4721         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4722
4723
4724         var Y = Roo.lib;
4725         var superclass = Y.Motion.superclass;
4726         var proto = Y.Motion.prototype;
4727
4728         proto.toString = function() {
4729             var el = this.getEl();
4730             var id = el.id || el.tagName;
4731             return ("Motion " + id);
4732         };
4733
4734         proto.patterns.points = /^points$/i;
4735
4736         proto.setAttribute = function(attr, val, unit) {
4737             if (this.patterns.points.test(attr)) {
4738                 unit = unit || 'px';
4739                 superclass.setAttribute.call(this, 'left', val[0], unit);
4740                 superclass.setAttribute.call(this, 'top', val[1], unit);
4741             } else {
4742                 superclass.setAttribute.call(this, attr, val, unit);
4743             }
4744         };
4745
4746         proto.getAttribute = function(attr) {
4747             if (this.patterns.points.test(attr)) {
4748                 var val = [
4749                         superclass.getAttribute.call(this, 'left'),
4750                         superclass.getAttribute.call(this, 'top')
4751                         ];
4752             } else {
4753                 val = superclass.getAttribute.call(this, attr);
4754             }
4755
4756             return val;
4757         };
4758
4759         proto.doMethod = function(attr, start, end) {
4760             var val = null;
4761
4762             if (this.patterns.points.test(attr)) {
4763                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4764                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4765             } else {
4766                 val = superclass.doMethod.call(this, attr, start, end);
4767             }
4768             return val;
4769         };
4770
4771         proto.setRuntimeAttribute = function(attr) {
4772             if (this.patterns.points.test(attr)) {
4773                 var el = this.getEl();
4774                 var attributes = this.attributes;
4775                 var start;
4776                 var control = attributes['points']['control'] || [];
4777                 var end;
4778                 var i, len;
4779
4780                 if (control.length > 0 && !(control[0] instanceof Array)) {
4781                     control = [control];
4782                 } else {
4783                     var tmp = [];
4784                     for (i = 0,len = control.length; i < len; ++i) {
4785                         tmp[i] = control[i];
4786                     }
4787                     control = tmp;
4788                 }
4789
4790                 Roo.fly(el).position();
4791
4792                 if (isset(attributes['points']['from'])) {
4793                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4794                 }
4795                 else {
4796                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4797                 }
4798
4799                 start = this.getAttribute('points');
4800
4801
4802                 if (isset(attributes['points']['to'])) {
4803                     end = translateValues.call(this, attributes['points']['to'], start);
4804
4805                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4806                     for (i = 0,len = control.length; i < len; ++i) {
4807                         control[i] = translateValues.call(this, control[i], start);
4808                     }
4809
4810
4811                 } else if (isset(attributes['points']['by'])) {
4812                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4813
4814                     for (i = 0,len = control.length; i < len; ++i) {
4815                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4816                     }
4817                 }
4818
4819                 this.runtimeAttributes[attr] = [start];
4820
4821                 if (control.length > 0) {
4822                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4823                 }
4824
4825                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4826             }
4827             else {
4828                 superclass.setRuntimeAttribute.call(this, attr);
4829             }
4830         };
4831
4832         var translateValues = function(val, start) {
4833             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4834             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4835
4836             return val;
4837         };
4838
4839         var isset = function(prop) {
4840             return (typeof prop !== 'undefined');
4841         };
4842     })();
4843 /*
4844  * Portions of this file are based on pieces of Yahoo User Interface Library
4845  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4846  * YUI licensed under the BSD License:
4847  * http://developer.yahoo.net/yui/license.txt
4848  * <script type="text/javascript">
4849  *
4850  */
4851     (function() {
4852         Roo.lib.Scroll = function(el, attributes, duration, method) {
4853             if (el) {
4854                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4855             }
4856         };
4857
4858         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4859
4860
4861         var Y = Roo.lib;
4862         var superclass = Y.Scroll.superclass;
4863         var proto = Y.Scroll.prototype;
4864
4865         proto.toString = function() {
4866             var el = this.getEl();
4867             var id = el.id || el.tagName;
4868             return ("Scroll " + id);
4869         };
4870
4871         proto.doMethod = function(attr, start, end) {
4872             var val = null;
4873
4874             if (attr == 'scroll') {
4875                 val = [
4876                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4877                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4878                         ];
4879
4880             } else {
4881                 val = superclass.doMethod.call(this, attr, start, end);
4882             }
4883             return val;
4884         };
4885
4886         proto.getAttribute = function(attr) {
4887             var val = null;
4888             var el = this.getEl();
4889
4890             if (attr == 'scroll') {
4891                 val = [ el.scrollLeft, el.scrollTop ];
4892             } else {
4893                 val = superclass.getAttribute.call(this, attr);
4894             }
4895
4896             return val;
4897         };
4898
4899         proto.setAttribute = function(attr, val, unit) {
4900             var el = this.getEl();
4901
4902             if (attr == 'scroll') {
4903                 el.scrollLeft = val[0];
4904                 el.scrollTop = val[1];
4905             } else {
4906                 superclass.setAttribute.call(this, attr, val, unit);
4907             }
4908         };
4909     })();
4910 /**
4911  * Originally based of this code... - refactored for Roo...
4912  * https://github.com/aaalsaleh/undo-manager
4913  
4914  * undo-manager.js
4915  * @author  Abdulrahman Alsaleh 
4916  * @copyright 2015 Abdulrahman Alsaleh 
4917  * @license  MIT License (c) 
4918  *
4919  * Hackily modifyed by alan@roojs.com
4920  *
4921  *
4922  *  
4923  *
4924  *  TOTALLY UNTESTED...
4925  *
4926  *  Documentation to be done....
4927  */
4928  
4929
4930 /**
4931 * @class Roo.lib.UndoManager
4932 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4933 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4934
4935  * Usage:
4936  * <pre><code>
4937
4938
4939 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4940  
4941 </code></pre>
4942
4943 * For more information see this blog post with examples:
4944 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4945      - Create Elements using DOM, HTML fragments and Templates</a>. 
4946 * @constructor
4947 * @param {Number} limit how far back to go ... use 1000?
4948 * @param {Object} scope usually use document..
4949 */
4950
4951 Roo.lib.UndoManager = function (limit, undoScopeHost)
4952 {
4953     this.stack = [];
4954     this.limit = limit;
4955     this.scope = undoScopeHost;
4956     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4957     if (this.fireEvent) {
4958         this.bindEvents();
4959     }
4960     this.reset();
4961     
4962 };
4963         
4964 Roo.lib.UndoManager.prototype = {
4965     
4966     limit : false,
4967     stack : false,
4968     scope :  false,
4969     fireEvent : false,
4970     position : 0,
4971     length : 0,
4972     
4973     
4974      /**
4975      * To push and execute a transaction, the method undoManager.transact
4976      * must be called by passing a transaction object as the first argument, and a merge
4977      * flag as the second argument. A transaction object has the following properties:
4978      *
4979      * Usage:
4980 <pre><code>
4981 undoManager.transact({
4982     label: 'Typing',
4983     execute: function() { ... },
4984     undo: function() { ... },
4985     // redo same as execute
4986     redo: function() { this.execute(); }
4987 }, false);
4988
4989 // merge transaction
4990 undoManager.transact({
4991     label: 'Typing',
4992     execute: function() { ... },  // this will be run...
4993     undo: function() { ... }, // what to do when undo is run.
4994     // redo same as execute
4995     redo: function() { this.execute(); }
4996 }, true); 
4997 </code></pre> 
4998      *
4999      * 
5000      * @param {Object} transaction The transaction to add to the stack.
5001      * @return {String} The HTML fragment
5002      */
5003     
5004     
5005     transact : function (transaction, merge)
5006     {
5007         if (arguments.length < 2) {
5008             throw new TypeError('Not enough arguments to UndoManager.transact.');
5009         }
5010
5011         transaction.execute();
5012
5013         this.stack.splice(0, this.position);
5014         if (merge && this.length) {
5015             this.stack[0].push(transaction);
5016         } else {
5017             this.stack.unshift([transaction]);
5018         }
5019     
5020         this.position = 0;
5021
5022         if (this.limit && this.stack.length > this.limit) {
5023             this.length = this.stack.length = this.limit;
5024         } else {
5025             this.length = this.stack.length;
5026         }
5027
5028         if (this.fireEvent) {
5029             this.scope.dispatchEvent(
5030                 new CustomEvent('DOMTransaction', {
5031                     detail: {
5032                         transactions: this.stack[0].slice()
5033                     },
5034                     bubbles: true,
5035                     cancelable: false
5036                 })
5037             );
5038         }
5039         
5040         //Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5041       
5042         
5043     },
5044
5045     undo : function ()
5046     {
5047         //Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5048         
5049         if (this.position < this.length) {
5050             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5051                 this.stack[this.position][i].undo();
5052             }
5053             this.position++;
5054
5055             if (this.fireEvent) {
5056                 this.scope.dispatchEvent(
5057                     new CustomEvent('undo', {
5058                         detail: {
5059                             transactions: this.stack[this.position - 1].slice()
5060                         },
5061                         bubbles: true,
5062                         cancelable: false
5063                     })
5064                 );
5065             }
5066         }
5067     },
5068
5069     redo : function ()
5070     {
5071         if (this.position > 0) {
5072             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5073                 this.stack[this.position - 1][i].redo();
5074             }
5075             this.position--;
5076
5077             if (this.fireEvent) {
5078                 this.scope.dispatchEvent(
5079                     new CustomEvent('redo', {
5080                         detail: {
5081                             transactions: this.stack[this.position].slice()
5082                         },
5083                         bubbles: true,
5084                         cancelable: false
5085                     })
5086                 );
5087             }
5088         }
5089     },
5090
5091     item : function (index)
5092     {
5093         if (index >= 0 && index < this.length) {
5094             return this.stack[index].slice();
5095         }
5096         return null;
5097     },
5098
5099     clearUndo : function () {
5100         this.stack.length = this.length = this.position;
5101     },
5102
5103     clearRedo : function () {
5104         this.stack.splice(0, this.position);
5105         this.position = 0;
5106         this.length = this.stack.length;
5107     },
5108     /**
5109      * Reset the undo - probaly done on load to clear all history.
5110      */
5111     reset : function()
5112     {
5113         this.stack = [];
5114         this.position = 0;
5115         this.length = 0;
5116         this.current_html = this.scope.innerHTML;
5117         if (this.timer !== false) {
5118             clearTimeout(this.timer);
5119         }
5120         this.timer = false;
5121         this.merge = false;
5122         this.addEvent();
5123         
5124     },
5125     current_html : '',
5126     timer : false,
5127     merge : false,
5128     
5129     
5130     // this will handle the undo/redo on the element.?
5131     bindEvents : function()
5132     {
5133         var el  = this.scope;
5134         el.undoManager = this;
5135         
5136         
5137         this.scope.addEventListener('keydown', function(e) {
5138             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5139                 if (e.shiftKey) {
5140                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5141                 } else {
5142                     el.undoManager.undo(); // Ctrl/Command + Z
5143                 }
5144         
5145                 e.preventDefault();
5146             }
5147         });
5148         /// ignore keyup..
5149         this.scope.addEventListener('keyup', function(e) {
5150             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5151                 e.preventDefault();
5152             }
5153         });
5154         
5155         
5156         
5157         var t = this;
5158         
5159         el.addEventListener('input', function(e) {
5160             if(el.innerHTML == t.current_html) {
5161                 return;
5162             }
5163             // only record events every second.
5164             if (t.timer !== false) {
5165                clearTimeout(t.timer);
5166                t.timer = false;
5167             }
5168             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5169             
5170             t.addEvent(t.merge);
5171             t.merge = true; // ignore changes happening every second..
5172         });
5173         },
5174     /**
5175      * Manually add an event.
5176      * Normall called without arguements - and it will just get added to the stack.
5177      * 
5178      */
5179     
5180     addEvent : function(merge)
5181     {
5182         //Roo.log("undomanager +" + (merge ? 'Y':'n'));
5183         // not sure if this should clear the timer 
5184         merge = typeof(merge) == 'undefined' ? false : merge; 
5185         
5186         this.scope.undoManager.transact({
5187             scope : this.scope,
5188             oldHTML: this.current_html,
5189             newHTML: this.scope.innerHTML,
5190             // nothing to execute (content already changed when input is fired)
5191             execute: function() { },
5192             undo: function() {
5193                 this.scope.innerHTML = this.current_html = this.oldHTML;
5194             },
5195             redo: function() {
5196                 this.scope.innerHTML = this.current_html = this.newHTML;
5197             }
5198         }, false); //merge);
5199         
5200         this.merge = merge;
5201         
5202         this.current_html = this.scope.innerHTML;
5203     }
5204     
5205     
5206      
5207     
5208     
5209     
5210 };
5211 /*
5212  * Based on:
5213  * Ext JS Library 1.1.1
5214  * Copyright(c) 2006-2007, Ext JS, LLC.
5215  *
5216  * Originally Released Under LGPL - original licence link has changed is not relivant.
5217  *
5218  * Fork - LGPL
5219  * <script type="text/javascript">
5220  */
5221
5222
5223 // nasty IE9 hack - what a pile of crap that is..
5224
5225  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5226     Range.prototype.createContextualFragment = function (html) {
5227         var doc = window.document;
5228         var container = doc.createElement("div");
5229         container.innerHTML = html;
5230         var frag = doc.createDocumentFragment(), n;
5231         while ((n = container.firstChild)) {
5232             frag.appendChild(n);
5233         }
5234         return frag;
5235     };
5236 }
5237
5238 /**
5239  * @class Roo.DomHelper
5240  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5241  * 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>.
5242  * @static
5243  */
5244 Roo.DomHelper = function(){
5245     var tempTableEl = null;
5246     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5247     var tableRe = /^table|tbody|tr|td$/i;
5248     var xmlns = {};
5249     // build as innerHTML where available
5250     /** @ignore */
5251     var createHtml = function(o){
5252         if(typeof o == 'string'){
5253             return o;
5254         }
5255         var b = "";
5256         if(!o.tag){
5257             o.tag = "div";
5258         }
5259         b += "<" + o.tag;
5260         for(var attr in o){
5261             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5262             if(attr == "style"){
5263                 var s = o["style"];
5264                 if(typeof s == "function"){
5265                     s = s.call();
5266                 }
5267                 if(typeof s == "string"){
5268                     b += ' style="' + s + '"';
5269                 }else if(typeof s == "object"){
5270                     b += ' style="';
5271                     for(var key in s){
5272                         if(typeof s[key] != "function"){
5273                             b += key + ":" + s[key] + ";";
5274                         }
5275                     }
5276                     b += '"';
5277                 }
5278             }else{
5279                 if(attr == "cls"){
5280                     b += ' class="' + o["cls"] + '"';
5281                 }else if(attr == "htmlFor"){
5282                     b += ' for="' + o["htmlFor"] + '"';
5283                 }else{
5284                     b += " " + attr + '="' + o[attr] + '"';
5285                 }
5286             }
5287         }
5288         if(emptyTags.test(o.tag)){
5289             b += "/>";
5290         }else{
5291             b += ">";
5292             var cn = o.children || o.cn;
5293             if(cn){
5294                 //http://bugs.kde.org/show_bug.cgi?id=71506
5295                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5296                     for(var i = 0, len = cn.length; i < len; i++) {
5297                         b += createHtml(cn[i], b);
5298                     }
5299                 }else{
5300                     b += createHtml(cn, b);
5301                 }
5302             }
5303             if(o.html){
5304                 b += o.html;
5305             }
5306             b += "</" + o.tag + ">";
5307         }
5308         return b;
5309     };
5310
5311     // build as dom
5312     /** @ignore */
5313     var createDom = function(o, parentNode){
5314          
5315         // defininition craeted..
5316         var ns = false;
5317         if (o.ns && o.ns != 'html') {
5318                
5319             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5320                 xmlns[o.ns] = o.xmlns;
5321                 ns = o.xmlns;
5322             }
5323             if (typeof(xmlns[o.ns]) == 'undefined') {
5324                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5325             }
5326             ns = xmlns[o.ns];
5327         }
5328         
5329         
5330         if (typeof(o) == 'string') {
5331             return parentNode.appendChild(document.createTextNode(o));
5332         }
5333         o.tag = o.tag || div;
5334         if (o.ns && Roo.isIE) {
5335             ns = false;
5336             o.tag = o.ns + ':' + o.tag;
5337             
5338         }
5339         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5340         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5341         for(var attr in o){
5342             
5343             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5344                     attr == "style" || typeof o[attr] == "function") { continue; }
5345                     
5346             if(attr=="cls" && Roo.isIE){
5347                 el.className = o["cls"];
5348             }else{
5349                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5350                 else { 
5351                     el[attr] = o[attr];
5352                 }
5353             }
5354         }
5355         Roo.DomHelper.applyStyles(el, o.style);
5356         var cn = o.children || o.cn;
5357         if(cn){
5358             //http://bugs.kde.org/show_bug.cgi?id=71506
5359              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5360                 for(var i = 0, len = cn.length; i < len; i++) {
5361                     createDom(cn[i], el);
5362                 }
5363             }else{
5364                 createDom(cn, el);
5365             }
5366         }
5367         if(o.html){
5368             el.innerHTML = o.html;
5369         }
5370         if(parentNode){
5371            parentNode.appendChild(el);
5372         }
5373         return el;
5374     };
5375
5376     var ieTable = function(depth, s, h, e){
5377         tempTableEl.innerHTML = [s, h, e].join('');
5378         var i = -1, el = tempTableEl;
5379         while(++i < depth && el.firstChild){
5380             el = el.firstChild;
5381         }
5382         return el;
5383     };
5384
5385     // kill repeat to save bytes
5386     var ts = '<table>',
5387         te = '</table>',
5388         tbs = ts+'<tbody>',
5389         tbe = '</tbody>'+te,
5390         trs = tbs + '<tr>',
5391         tre = '</tr>'+tbe;
5392
5393     /**
5394      * @ignore
5395      * Nasty code for IE's broken table implementation
5396      */
5397     var insertIntoTable = function(tag, where, el, html){
5398         if(!tempTableEl){
5399             tempTableEl = document.createElement('div');
5400         }
5401         var node;
5402         var before = null;
5403         if(tag == 'td'){
5404             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5405                 return;
5406             }
5407             if(where == 'beforebegin'){
5408                 before = el;
5409                 el = el.parentNode;
5410             } else{
5411                 before = el.nextSibling;
5412                 el = el.parentNode;
5413             }
5414             node = ieTable(4, trs, html, tre);
5415         }
5416         else if(tag == 'tr'){
5417             if(where == 'beforebegin'){
5418                 before = el;
5419                 el = el.parentNode;
5420                 node = ieTable(3, tbs, html, tbe);
5421             } else if(where == 'afterend'){
5422                 before = el.nextSibling;
5423                 el = el.parentNode;
5424                 node = ieTable(3, tbs, html, tbe);
5425             } else{ // INTO a TR
5426                 if(where == 'afterbegin'){
5427                     before = el.firstChild;
5428                 }
5429                 node = ieTable(4, trs, html, tre);
5430             }
5431         } else if(tag == 'tbody'){
5432             if(where == 'beforebegin'){
5433                 before = el;
5434                 el = el.parentNode;
5435                 node = ieTable(2, ts, html, te);
5436             } else if(where == 'afterend'){
5437                 before = el.nextSibling;
5438                 el = el.parentNode;
5439                 node = ieTable(2, ts, html, te);
5440             } else{
5441                 if(where == 'afterbegin'){
5442                     before = el.firstChild;
5443                 }
5444                 node = ieTable(3, tbs, html, tbe);
5445             }
5446         } else{ // TABLE
5447             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5448                 return;
5449             }
5450             if(where == 'afterbegin'){
5451                 before = el.firstChild;
5452             }
5453             node = ieTable(2, ts, html, te);
5454         }
5455         el.insertBefore(node, before);
5456         return node;
5457     };
5458     
5459     // this is a bit like the react update code...
5460     // 
5461     
5462     var updateNode = function(from, to)
5463     {
5464         // should we handle non-standard elements?
5465         Roo.log(["UpdateNode" , from, to]);
5466         if (from.nodeType != to.nodeType) {
5467             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5468             from.parentNode.replaceChild(to, from);
5469         }
5470         
5471         if (from.nodeType == 3) {
5472             // assume it's text?!
5473             if (from.data == to.data) {
5474                 return;
5475             }
5476             from.data = to.data;
5477             return;
5478         }
5479         
5480         // assume 'to' doesnt have '1/3 nodetypes!
5481         if (from.nodeType !=1 || from.tagName != to.tagName) {
5482             Roo.log(["ReplaceChild" , from, to ]);
5483             from.parentNode.replaceChild(to, from);
5484             return;
5485         }
5486         // compare attributes
5487         var ar = Array.from(from.attributes);
5488         for(var i = 0; i< ar.length;i++) {
5489             if (to.hasAttribute(ar[i].name)) {
5490                 continue;
5491             }
5492             if (ar[i].name == 'id') { // always keep ids?
5493                continue;
5494             }
5495             //if (ar[i].name == 'style') {
5496             //   throw "style removed?";
5497             //}
5498             Roo.log("removeAttribute" + ar[i].name);
5499             from.removeAttribute(ar[i].name);
5500         }
5501         ar = to.attributes;
5502         for(var i = 0; i< ar.length;i++) {
5503             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5504                 Roo.log("skipAttribute " + ar[i].name  + '=' + to.getAttribute(ar[i].name));
5505                 continue;
5506             }
5507             Roo.log("updateAttribute " + ar[i].name + '=>' + to.getAttribute(ar[i].name));
5508             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5509         }
5510         // children
5511         var far = Array.from(from.childNodes);
5512         var tar = Array.from(to.childNodes);
5513         // if the lengths are different.. then it's probably a editable content change, rather than
5514         // a change of the block definition..
5515         
5516         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5517          /*if (from.innerHTML == to.innerHTML) {
5518             return;
5519         }
5520         if (far.length != tar.length) {
5521             from.innerHTML = to.innerHTML;
5522             return;
5523         }
5524         */
5525         
5526         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5527             if (i >= far.length) {
5528                 from.appendChild(tar[i]);
5529                 Roo.log(["add", tar[i]]);
5530                 
5531             } else if ( i  >= tar.length) {
5532                 from.removeChild(far[i]);
5533                 Roo.log(["remove", far[i]]);
5534             } else {
5535                 
5536                 updateNode(far[i], tar[i]);
5537             }    
5538         }
5539         
5540         
5541         
5542         
5543     };
5544     
5545     
5546
5547     return {
5548         /** True to force the use of DOM instead of html fragments @type Boolean */
5549         useDom : false,
5550     
5551         /**
5552          * Returns the markup for the passed Element(s) config
5553          * @param {Object} o The Dom object spec (and children)
5554          * @return {String}
5555          */
5556         markup : function(o){
5557             return createHtml(o);
5558         },
5559     
5560         /**
5561          * Applies a style specification to an element
5562          * @param {String/HTMLElement} el The element to apply styles to
5563          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5564          * a function which returns such a specification.
5565          */
5566         applyStyles : function(el, styles){
5567             if(styles){
5568                el = Roo.fly(el);
5569                if(typeof styles == "string"){
5570                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5571                    var matches;
5572                    while ((matches = re.exec(styles)) != null){
5573                        el.setStyle(matches[1], matches[2]);
5574                    }
5575                }else if (typeof styles == "object"){
5576                    for (var style in styles){
5577                       el.setStyle(style, styles[style]);
5578                    }
5579                }else if (typeof styles == "function"){
5580                     Roo.DomHelper.applyStyles(el, styles.call());
5581                }
5582             }
5583         },
5584     
5585         /**
5586          * Inserts an HTML fragment into the Dom
5587          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5588          * @param {HTMLElement} el The context element
5589          * @param {String} html The HTML fragmenet
5590          * @return {HTMLElement} The new node
5591          */
5592         insertHtml : function(where, el, html){
5593             where = where.toLowerCase();
5594             if(el.insertAdjacentHTML){
5595                 if(tableRe.test(el.tagName)){
5596                     var rs;
5597                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5598                         return rs;
5599                     }
5600                 }
5601                 switch(where){
5602                     case "beforebegin":
5603                         el.insertAdjacentHTML('BeforeBegin', html);
5604                         return el.previousSibling;
5605                     case "afterbegin":
5606                         el.insertAdjacentHTML('AfterBegin', html);
5607                         return el.firstChild;
5608                     case "beforeend":
5609                         el.insertAdjacentHTML('BeforeEnd', html);
5610                         return el.lastChild;
5611                     case "afterend":
5612                         el.insertAdjacentHTML('AfterEnd', html);
5613                         return el.nextSibling;
5614                 }
5615                 throw 'Illegal insertion point -> "' + where + '"';
5616             }
5617             var range = el.ownerDocument.createRange();
5618             var frag;
5619             switch(where){
5620                  case "beforebegin":
5621                     range.setStartBefore(el);
5622                     frag = range.createContextualFragment(html);
5623                     el.parentNode.insertBefore(frag, el);
5624                     return el.previousSibling;
5625                  case "afterbegin":
5626                     if(el.firstChild){
5627                         range.setStartBefore(el.firstChild);
5628                         frag = range.createContextualFragment(html);
5629                         el.insertBefore(frag, el.firstChild);
5630                         return el.firstChild;
5631                     }else{
5632                         el.innerHTML = html;
5633                         return el.firstChild;
5634                     }
5635                 case "beforeend":
5636                     if(el.lastChild){
5637                         range.setStartAfter(el.lastChild);
5638                         frag = range.createContextualFragment(html);
5639                         el.appendChild(frag);
5640                         return el.lastChild;
5641                     }else{
5642                         el.innerHTML = html;
5643                         return el.lastChild;
5644                     }
5645                 case "afterend":
5646                     range.setStartAfter(el);
5647                     frag = range.createContextualFragment(html);
5648                     el.parentNode.insertBefore(frag, el.nextSibling);
5649                     return el.nextSibling;
5650                 }
5651                 throw 'Illegal insertion point -> "' + where + '"';
5652         },
5653     
5654         /**
5655          * Creates new Dom element(s) and inserts them before el
5656          * @param {String/HTMLElement/Element} el The context element
5657          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5658          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5659          * @return {HTMLElement/Roo.Element} The new node
5660          */
5661         insertBefore : function(el, o, returnElement){
5662             return this.doInsert(el, o, returnElement, "beforeBegin");
5663         },
5664     
5665         /**
5666          * Creates new Dom element(s) and inserts them after el
5667          * @param {String/HTMLElement/Element} el The context element
5668          * @param {Object} o The Dom object spec (and children)
5669          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5670          * @return {HTMLElement/Roo.Element} The new node
5671          */
5672         insertAfter : function(el, o, returnElement){
5673             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5674         },
5675     
5676         /**
5677          * Creates new Dom element(s) and inserts them as the first child of el
5678          * @param {String/HTMLElement/Element} el The context element
5679          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5680          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5681          * @return {HTMLElement/Roo.Element} The new node
5682          */
5683         insertFirst : function(el, o, returnElement){
5684             return this.doInsert(el, o, returnElement, "afterBegin");
5685         },
5686     
5687         // private
5688         doInsert : function(el, o, returnElement, pos, sibling){
5689             el = Roo.getDom(el);
5690             var newNode;
5691             if(this.useDom || o.ns){
5692                 newNode = createDom(o, null);
5693                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5694             }else{
5695                 var html = createHtml(o);
5696                 newNode = this.insertHtml(pos, el, html);
5697             }
5698             return returnElement ? Roo.get(newNode, true) : newNode;
5699         },
5700     
5701         /**
5702          * Creates new Dom element(s) and appends them to el
5703          * @param {String/HTMLElement/Element} el The context element
5704          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5705          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5706          * @return {HTMLElement/Roo.Element} The new node
5707          */
5708         append : function(el, o, returnElement){
5709             el = Roo.getDom(el);
5710             var newNode;
5711             if(this.useDom || o.ns){
5712                 newNode = createDom(o, null);
5713                 el.appendChild(newNode);
5714             }else{
5715                 var html = createHtml(o);
5716                 newNode = this.insertHtml("beforeEnd", el, html);
5717             }
5718             return returnElement ? Roo.get(newNode, true) : newNode;
5719         },
5720     
5721         /**
5722          * Creates new Dom element(s) and overwrites the contents of el with them
5723          * @param {String/HTMLElement/Element} el The context element
5724          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5725          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5726          * @return {HTMLElement/Roo.Element} The new node
5727          */
5728         overwrite : function(el, o, returnElement)
5729         {
5730             el = Roo.getDom(el);
5731             if (o.ns) {
5732               
5733                 while (el.childNodes.length) {
5734                     el.removeChild(el.firstChild);
5735                 }
5736                 createDom(o, el);
5737             } else {
5738                 el.innerHTML = createHtml(o);   
5739             }
5740             
5741             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5742         },
5743     
5744         /**
5745          * Creates a new Roo.DomHelper.Template from the Dom object spec
5746          * @param {Object} o The Dom object spec (and children)
5747          * @return {Roo.DomHelper.Template} The new template
5748          */
5749         createTemplate : function(o){
5750             var html = createHtml(o);
5751             return new Roo.Template(html);
5752         },
5753          /**
5754          * Updates the first element with the spec from the o (replacing if necessary)
5755          * This iterates through the children, and updates attributes / children etc..
5756          * @param {String/HTMLElement/Element} el The context element
5757          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5758          */
5759         
5760         update : function(el, o)
5761         {
5762             updateNode(Roo.getDom(el), createDom(o));
5763             
5764         }
5765         
5766         
5767     };
5768 }();
5769 /*
5770  * Based on:
5771  * Ext JS Library 1.1.1
5772  * Copyright(c) 2006-2007, Ext JS, LLC.
5773  *
5774  * Originally Released Under LGPL - original licence link has changed is not relivant.
5775  *
5776  * Fork - LGPL
5777  * <script type="text/javascript">
5778  */
5779  
5780 /**
5781 * @class Roo.Template
5782 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5783 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5784 * Usage:
5785 <pre><code>
5786 var t = new Roo.Template({
5787     html :  '&lt;div name="{id}"&gt;' + 
5788         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5789         '&lt;/div&gt;',
5790     myformat: function (value, allValues) {
5791         return 'XX' + value;
5792     }
5793 });
5794 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5795 </code></pre>
5796 * For more information see this blog post with examples:
5797 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5798      - Create Elements using DOM, HTML fragments and Templates</a>. 
5799 * @constructor
5800 * @param {Object} cfg - Configuration object.
5801 */
5802 Roo.Template = function(cfg){
5803     // BC!
5804     if(cfg instanceof Array){
5805         cfg = cfg.join("");
5806     }else if(arguments.length > 1){
5807         cfg = Array.prototype.join.call(arguments, "");
5808     }
5809     
5810     
5811     if (typeof(cfg) == 'object') {
5812         Roo.apply(this,cfg)
5813     } else {
5814         // bc
5815         this.html = cfg;
5816     }
5817     if (this.url) {
5818         this.load();
5819     }
5820     
5821 };
5822 Roo.Template.prototype = {
5823     
5824     /**
5825      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5826      */
5827     onLoad : false,
5828     
5829     
5830     /**
5831      * @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..
5832      *                    it should be fixed so that template is observable...
5833      */
5834     url : false,
5835     /**
5836      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5837      */
5838     html : '',
5839     
5840     
5841     compiled : false,
5842     loaded : false,
5843     /**
5844      * Returns an HTML fragment of this template with the specified values applied.
5845      * @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'})
5846      * @return {String} The HTML fragment
5847      */
5848     
5849    
5850     
5851     applyTemplate : function(values){
5852         //Roo.log(["applyTemplate", values]);
5853         try {
5854            
5855             if(this.compiled){
5856                 return this.compiled(values);
5857             }
5858             var useF = this.disableFormats !== true;
5859             var fm = Roo.util.Format, tpl = this;
5860             var fn = function(m, name, format, args){
5861                 if(format && useF){
5862                     if(format.substr(0, 5) == "this."){
5863                         return tpl.call(format.substr(5), values[name], values);
5864                     }else{
5865                         if(args){
5866                             // quoted values are required for strings in compiled templates, 
5867                             // but for non compiled we need to strip them
5868                             // quoted reversed for jsmin
5869                             var re = /^\s*['"](.*)["']\s*$/;
5870                             args = args.split(',');
5871                             for(var i = 0, len = args.length; i < len; i++){
5872                                 args[i] = args[i].replace(re, "$1");
5873                             }
5874                             args = [values[name]].concat(args);
5875                         }else{
5876                             args = [values[name]];
5877                         }
5878                         return fm[format].apply(fm, args);
5879                     }
5880                 }else{
5881                     return values[name] !== undefined ? values[name] : "";
5882                 }
5883             };
5884             return this.html.replace(this.re, fn);
5885         } catch (e) {
5886             Roo.log(e);
5887             throw e;
5888         }
5889          
5890     },
5891     
5892     loading : false,
5893       
5894     load : function ()
5895     {
5896          
5897         if (this.loading) {
5898             return;
5899         }
5900         var _t = this;
5901         
5902         this.loading = true;
5903         this.compiled = false;
5904         
5905         var cx = new Roo.data.Connection();
5906         cx.request({
5907             url : this.url,
5908             method : 'GET',
5909             success : function (response) {
5910                 _t.loading = false;
5911                 _t.url = false;
5912                 
5913                 _t.set(response.responseText,true);
5914                 _t.loaded = true;
5915                 if (_t.onLoad) {
5916                     _t.onLoad();
5917                 }
5918              },
5919             failure : function(response) {
5920                 Roo.log("Template failed to load from " + _t.url);
5921                 _t.loading = false;
5922             }
5923         });
5924     },
5925
5926     /**
5927      * Sets the HTML used as the template and optionally compiles it.
5928      * @param {String} html
5929      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5930      * @return {Roo.Template} this
5931      */
5932     set : function(html, compile){
5933         this.html = html;
5934         this.compiled = false;
5935         if(compile){
5936             this.compile();
5937         }
5938         return this;
5939     },
5940     
5941     /**
5942      * True to disable format functions (defaults to false)
5943      * @type Boolean
5944      */
5945     disableFormats : false,
5946     
5947     /**
5948     * The regular expression used to match template variables 
5949     * @type RegExp
5950     * @property 
5951     */
5952     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5953     
5954     /**
5955      * Compiles the template into an internal function, eliminating the RegEx overhead.
5956      * @return {Roo.Template} this
5957      */
5958     compile : function(){
5959         var fm = Roo.util.Format;
5960         var useF = this.disableFormats !== true;
5961         var sep = Roo.isGecko ? "+" : ",";
5962         var fn = function(m, name, format, args){
5963             if(format && useF){
5964                 args = args ? ',' + args : "";
5965                 if(format.substr(0, 5) != "this."){
5966                     format = "fm." + format + '(';
5967                 }else{
5968                     format = 'this.call("'+ format.substr(5) + '", ';
5969                     args = ", values";
5970                 }
5971             }else{
5972                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5973             }
5974             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5975         };
5976         var body;
5977         // branched to use + in gecko and [].join() in others
5978         if(Roo.isGecko){
5979             body = "this.compiled = function(values){ return '" +
5980                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5981                     "';};";
5982         }else{
5983             body = ["this.compiled = function(values){ return ['"];
5984             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5985             body.push("'].join('');};");
5986             body = body.join('');
5987         }
5988         /**
5989          * eval:var:values
5990          * eval:var:fm
5991          */
5992         eval(body);
5993         return this;
5994     },
5995     
5996     // private function used to call members
5997     call : function(fnName, value, allValues){
5998         return this[fnName](value, allValues);
5999     },
6000     
6001     /**
6002      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
6003      * @param {String/HTMLElement/Roo.Element} el The context element
6004      * @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'})
6005      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6006      * @return {HTMLElement/Roo.Element} The new node or Element
6007      */
6008     insertFirst: function(el, values, returnElement){
6009         return this.doInsert('afterBegin', el, values, returnElement);
6010     },
6011
6012     /**
6013      * Applies the supplied values to the template and inserts the new node(s) before el.
6014      * @param {String/HTMLElement/Roo.Element} el The context element
6015      * @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'})
6016      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6017      * @return {HTMLElement/Roo.Element} The new node or Element
6018      */
6019     insertBefore: function(el, values, returnElement){
6020         return this.doInsert('beforeBegin', el, values, returnElement);
6021     },
6022
6023     /**
6024      * Applies the supplied values to the template and inserts the new node(s) after el.
6025      * @param {String/HTMLElement/Roo.Element} el The context element
6026      * @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'})
6027      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6028      * @return {HTMLElement/Roo.Element} The new node or Element
6029      */
6030     insertAfter : function(el, values, returnElement){
6031         return this.doInsert('afterEnd', el, values, returnElement);
6032     },
6033     
6034     /**
6035      * Applies the supplied values to the template and appends the new node(s) to el.
6036      * @param {String/HTMLElement/Roo.Element} el The context element
6037      * @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'})
6038      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6039      * @return {HTMLElement/Roo.Element} The new node or Element
6040      */
6041     append : function(el, values, returnElement){
6042         return this.doInsert('beforeEnd', el, values, returnElement);
6043     },
6044
6045     doInsert : function(where, el, values, returnEl){
6046         el = Roo.getDom(el);
6047         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6048         return returnEl ? Roo.get(newNode, true) : newNode;
6049     },
6050
6051     /**
6052      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6053      * @param {String/HTMLElement/Roo.Element} el The context element
6054      * @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'})
6055      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6056      * @return {HTMLElement/Roo.Element} The new node or Element
6057      */
6058     overwrite : function(el, values, returnElement){
6059         el = Roo.getDom(el);
6060         el.innerHTML = this.applyTemplate(values);
6061         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6062     }
6063 };
6064 /**
6065  * Alias for {@link #applyTemplate}
6066  * @method
6067  */
6068 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6069
6070 // backwards compat
6071 Roo.DomHelper.Template = Roo.Template;
6072
6073 /**
6074  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6075  * @param {String/HTMLElement} el A DOM element or its id
6076  * @returns {Roo.Template} The created template
6077  * @static
6078  */
6079 Roo.Template.from = function(el){
6080     el = Roo.getDom(el);
6081     return new Roo.Template(el.value || el.innerHTML);
6082 };/*
6083  * Based on:
6084  * Ext JS Library 1.1.1
6085  * Copyright(c) 2006-2007, Ext JS, LLC.
6086  *
6087  * Originally Released Under LGPL - original licence link has changed is not relivant.
6088  *
6089  * Fork - LGPL
6090  * <script type="text/javascript">
6091  */
6092  
6093
6094 /*
6095  * This is code is also distributed under MIT license for use
6096  * with jQuery and prototype JavaScript libraries.
6097  */
6098 /**
6099  * @class Roo.DomQuery
6100 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).
6101 <p>
6102 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>
6103
6104 <p>
6105 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.
6106 </p>
6107 <h4>Element Selectors:</h4>
6108 <ul class="list">
6109     <li> <b>*</b> any element</li>
6110     <li> <b>E</b> an element with the tag E</li>
6111     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6112     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6113     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6114     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6115 </ul>
6116 <h4>Attribute Selectors:</h4>
6117 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6118 <ul class="list">
6119     <li> <b>E[foo]</b> has an attribute "foo"</li>
6120     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6121     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6122     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6123     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6124     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6125     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6126 </ul>
6127 <h4>Pseudo Classes:</h4>
6128 <ul class="list">
6129     <li> <b>E:first-child</b> E is the first child of its parent</li>
6130     <li> <b>E:last-child</b> E is the last child of its parent</li>
6131     <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>
6132     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6133     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6134     <li> <b>E:only-child</b> E is the only child of its parent</li>
6135     <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>
6136     <li> <b>E:first</b> the first E in the resultset</li>
6137     <li> <b>E:last</b> the last E in the resultset</li>
6138     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6139     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6140     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6141     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6142     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6143     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6144     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6145     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6146     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6147 </ul>
6148 <h4>CSS Value Selectors:</h4>
6149 <ul class="list">
6150     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6151     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6152     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6153     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6154     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6155     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6156 </ul>
6157  * @static
6158  */
6159 Roo.DomQuery = function(){
6160     var cache = {}, simpleCache = {}, valueCache = {};
6161     var nonSpace = /\S/;
6162     var trimRe = /^\s+|\s+$/g;
6163     var tplRe = /\{(\d+)\}/g;
6164     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6165     var tagTokenRe = /^(#)?([\w-\*]+)/;
6166     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6167
6168     function child(p, index){
6169         var i = 0;
6170         var n = p.firstChild;
6171         while(n){
6172             if(n.nodeType == 1){
6173                if(++i == index){
6174                    return n;
6175                }
6176             }
6177             n = n.nextSibling;
6178         }
6179         return null;
6180     };
6181
6182     function next(n){
6183         while((n = n.nextSibling) && n.nodeType != 1);
6184         return n;
6185     };
6186
6187     function prev(n){
6188         while((n = n.previousSibling) && n.nodeType != 1);
6189         return n;
6190     };
6191
6192     function children(d){
6193         var n = d.firstChild, ni = -1;
6194             while(n){
6195                 var nx = n.nextSibling;
6196                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6197                     d.removeChild(n);
6198                 }else{
6199                     n.nodeIndex = ++ni;
6200                 }
6201                 n = nx;
6202             }
6203             return this;
6204         };
6205
6206     function byClassName(c, a, v){
6207         if(!v){
6208             return c;
6209         }
6210         var r = [], ri = -1, cn;
6211         for(var i = 0, ci; ci = c[i]; i++){
6212             
6213             
6214             if((' '+
6215                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6216                  +' ').indexOf(v) != -1){
6217                 r[++ri] = ci;
6218             }
6219         }
6220         return r;
6221     };
6222
6223     function attrValue(n, attr){
6224         if(!n.tagName && typeof n.length != "undefined"){
6225             n = n[0];
6226         }
6227         if(!n){
6228             return null;
6229         }
6230         if(attr == "for"){
6231             return n.htmlFor;
6232         }
6233         if(attr == "class" || attr == "className"){
6234             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6235         }
6236         return n.getAttribute(attr) || n[attr];
6237
6238     };
6239
6240     function getNodes(ns, mode, tagName){
6241         var result = [], ri = -1, cs;
6242         if(!ns){
6243             return result;
6244         }
6245         tagName = tagName || "*";
6246         if(typeof ns.getElementsByTagName != "undefined"){
6247             ns = [ns];
6248         }
6249         if(!mode){
6250             for(var i = 0, ni; ni = ns[i]; i++){
6251                 cs = ni.getElementsByTagName(tagName);
6252                 for(var j = 0, ci; ci = cs[j]; j++){
6253                     result[++ri] = ci;
6254                 }
6255             }
6256         }else if(mode == "/" || mode == ">"){
6257             var utag = tagName.toUpperCase();
6258             for(var i = 0, ni, cn; ni = ns[i]; i++){
6259                 cn = ni.children || ni.childNodes;
6260                 for(var j = 0, cj; cj = cn[j]; j++){
6261                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6262                         result[++ri] = cj;
6263                     }
6264                 }
6265             }
6266         }else if(mode == "+"){
6267             var utag = tagName.toUpperCase();
6268             for(var i = 0, n; n = ns[i]; i++){
6269                 while((n = n.nextSibling) && n.nodeType != 1);
6270                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6271                     result[++ri] = n;
6272                 }
6273             }
6274         }else if(mode == "~"){
6275             for(var i = 0, n; n = ns[i]; i++){
6276                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6277                 if(n){
6278                     result[++ri] = n;
6279                 }
6280             }
6281         }
6282         return result;
6283     };
6284
6285     function concat(a, b){
6286         if(b.slice){
6287             return a.concat(b);
6288         }
6289         for(var i = 0, l = b.length; i < l; i++){
6290             a[a.length] = b[i];
6291         }
6292         return a;
6293     }
6294
6295     function byTag(cs, tagName){
6296         if(cs.tagName || cs == document){
6297             cs = [cs];
6298         }
6299         if(!tagName){
6300             return cs;
6301         }
6302         var r = [], ri = -1;
6303         tagName = tagName.toLowerCase();
6304         for(var i = 0, ci; ci = cs[i]; i++){
6305             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6306                 r[++ri] = ci;
6307             }
6308         }
6309         return r;
6310     };
6311
6312     function byId(cs, attr, id){
6313         if(cs.tagName || cs == document){
6314             cs = [cs];
6315         }
6316         if(!id){
6317             return cs;
6318         }
6319         var r = [], ri = -1;
6320         for(var i = 0,ci; ci = cs[i]; i++){
6321             if(ci && ci.id == id){
6322                 r[++ri] = ci;
6323                 return r;
6324             }
6325         }
6326         return r;
6327     };
6328
6329     function byAttribute(cs, attr, value, op, custom){
6330         var r = [], ri = -1, st = custom=="{";
6331         var f = Roo.DomQuery.operators[op];
6332         for(var i = 0, ci; ci = cs[i]; i++){
6333             var a;
6334             if(st){
6335                 a = Roo.DomQuery.getStyle(ci, attr);
6336             }
6337             else if(attr == "class" || attr == "className"){
6338                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6339             }else if(attr == "for"){
6340                 a = ci.htmlFor;
6341             }else if(attr == "href"){
6342                 a = ci.getAttribute("href", 2);
6343             }else{
6344                 a = ci.getAttribute(attr);
6345             }
6346             if((f && f(a, value)) || (!f && a)){
6347                 r[++ri] = ci;
6348             }
6349         }
6350         return r;
6351     };
6352
6353     function byPseudo(cs, name, value){
6354         return Roo.DomQuery.pseudos[name](cs, value);
6355     };
6356
6357     // This is for IE MSXML which does not support expandos.
6358     // IE runs the same speed using setAttribute, however FF slows way down
6359     // and Safari completely fails so they need to continue to use expandos.
6360     var isIE = window.ActiveXObject ? true : false;
6361
6362     // this eval is stop the compressor from
6363     // renaming the variable to something shorter
6364     
6365     /** eval:var:batch */
6366     var batch = 30803; 
6367
6368     var key = 30803;
6369
6370     function nodupIEXml(cs){
6371         var d = ++key;
6372         cs[0].setAttribute("_nodup", d);
6373         var r = [cs[0]];
6374         for(var i = 1, len = cs.length; i < len; i++){
6375             var c = cs[i];
6376             if(!c.getAttribute("_nodup") != d){
6377                 c.setAttribute("_nodup", d);
6378                 r[r.length] = c;
6379             }
6380         }
6381         for(var i = 0, len = cs.length; i < len; i++){
6382             cs[i].removeAttribute("_nodup");
6383         }
6384         return r;
6385     }
6386
6387     function nodup(cs){
6388         if(!cs){
6389             return [];
6390         }
6391         var len = cs.length, c, i, r = cs, cj, ri = -1;
6392         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6393             return cs;
6394         }
6395         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6396             return nodupIEXml(cs);
6397         }
6398         var d = ++key;
6399         cs[0]._nodup = d;
6400         for(i = 1; c = cs[i]; i++){
6401             if(c._nodup != d){
6402                 c._nodup = d;
6403             }else{
6404                 r = [];
6405                 for(var j = 0; j < i; j++){
6406                     r[++ri] = cs[j];
6407                 }
6408                 for(j = i+1; cj = cs[j]; j++){
6409                     if(cj._nodup != d){
6410                         cj._nodup = d;
6411                         r[++ri] = cj;
6412                     }
6413                 }
6414                 return r;
6415             }
6416         }
6417         return r;
6418     }
6419
6420     function quickDiffIEXml(c1, c2){
6421         var d = ++key;
6422         for(var i = 0, len = c1.length; i < len; i++){
6423             c1[i].setAttribute("_qdiff", d);
6424         }
6425         var r = [];
6426         for(var i = 0, len = c2.length; i < len; i++){
6427             if(c2[i].getAttribute("_qdiff") != d){
6428                 r[r.length] = c2[i];
6429             }
6430         }
6431         for(var i = 0, len = c1.length; i < len; i++){
6432            c1[i].removeAttribute("_qdiff");
6433         }
6434         return r;
6435     }
6436
6437     function quickDiff(c1, c2){
6438         var len1 = c1.length;
6439         if(!len1){
6440             return c2;
6441         }
6442         if(isIE && c1[0].selectSingleNode){
6443             return quickDiffIEXml(c1, c2);
6444         }
6445         var d = ++key;
6446         for(var i = 0; i < len1; i++){
6447             c1[i]._qdiff = d;
6448         }
6449         var r = [];
6450         for(var i = 0, len = c2.length; i < len; i++){
6451             if(c2[i]._qdiff != d){
6452                 r[r.length] = c2[i];
6453             }
6454         }
6455         return r;
6456     }
6457
6458     function quickId(ns, mode, root, id){
6459         if(ns == root){
6460            var d = root.ownerDocument || root;
6461            return d.getElementById(id);
6462         }
6463         ns = getNodes(ns, mode, "*");
6464         return byId(ns, null, id);
6465     }
6466
6467     return {
6468         getStyle : function(el, name){
6469             return Roo.fly(el).getStyle(name);
6470         },
6471         /**
6472          * Compiles a selector/xpath query into a reusable function. The returned function
6473          * takes one parameter "root" (optional), which is the context node from where the query should start.
6474          * @param {String} selector The selector/xpath query
6475          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6476          * @return {Function}
6477          */
6478         compile : function(path, type){
6479             type = type || "select";
6480             
6481             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6482             var q = path, mode, lq;
6483             var tk = Roo.DomQuery.matchers;
6484             var tklen = tk.length;
6485             var mm;
6486
6487             // accept leading mode switch
6488             var lmode = q.match(modeRe);
6489             if(lmode && lmode[1]){
6490                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6491                 q = q.replace(lmode[1], "");
6492             }
6493             // strip leading slashes
6494             while(path.substr(0, 1)=="/"){
6495                 path = path.substr(1);
6496             }
6497
6498             while(q && lq != q){
6499                 lq = q;
6500                 var tm = q.match(tagTokenRe);
6501                 if(type == "select"){
6502                     if(tm){
6503                         if(tm[1] == "#"){
6504                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6505                         }else{
6506                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6507                         }
6508                         q = q.replace(tm[0], "");
6509                     }else if(q.substr(0, 1) != '@'){
6510                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6511                     }
6512                 }else{
6513                     if(tm){
6514                         if(tm[1] == "#"){
6515                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6516                         }else{
6517                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6518                         }
6519                         q = q.replace(tm[0], "");
6520                     }
6521                 }
6522                 while(!(mm = q.match(modeRe))){
6523                     var matched = false;
6524                     for(var j = 0; j < tklen; j++){
6525                         var t = tk[j];
6526                         var m = q.match(t.re);
6527                         if(m){
6528                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6529                                                     return m[i];
6530                                                 });
6531                             q = q.replace(m[0], "");
6532                             matched = true;
6533                             break;
6534                         }
6535                     }
6536                     // prevent infinite loop on bad selector
6537                     if(!matched){
6538                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6539                     }
6540                 }
6541                 if(mm[1]){
6542                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6543                     q = q.replace(mm[1], "");
6544                 }
6545             }
6546             fn[fn.length] = "return nodup(n);\n}";
6547             
6548              /** 
6549               * list of variables that need from compression as they are used by eval.
6550              *  eval:var:batch 
6551              *  eval:var:nodup
6552              *  eval:var:byTag
6553              *  eval:var:ById
6554              *  eval:var:getNodes
6555              *  eval:var:quickId
6556              *  eval:var:mode
6557              *  eval:var:root
6558              *  eval:var:n
6559              *  eval:var:byClassName
6560              *  eval:var:byPseudo
6561              *  eval:var:byAttribute
6562              *  eval:var:attrValue
6563              * 
6564              **/ 
6565             eval(fn.join(""));
6566             return f;
6567         },
6568
6569         /**
6570          * Selects a group of elements.
6571          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6572          * @param {Node} root (optional) The start of the query (defaults to document).
6573          * @return {Array}
6574          */
6575         select : function(path, root, type){
6576             if(!root || root == document){
6577                 root = document;
6578             }
6579             if(typeof root == "string"){
6580                 root = document.getElementById(root);
6581             }
6582             var paths = path.split(",");
6583             var results = [];
6584             for(var i = 0, len = paths.length; i < len; i++){
6585                 var p = paths[i].replace(trimRe, "");
6586                 if(!cache[p]){
6587                     cache[p] = Roo.DomQuery.compile(p);
6588                     if(!cache[p]){
6589                         throw p + " is not a valid selector";
6590                     }
6591                 }
6592                 var result = cache[p](root);
6593                 if(result && result != document){
6594                     results = results.concat(result);
6595                 }
6596             }
6597             if(paths.length > 1){
6598                 return nodup(results);
6599             }
6600             return results;
6601         },
6602
6603         /**
6604          * Selects a single element.
6605          * @param {String} selector The selector/xpath query
6606          * @param {Node} root (optional) The start of the query (defaults to document).
6607          * @return {Element}
6608          */
6609         selectNode : function(path, root){
6610             return Roo.DomQuery.select(path, root)[0];
6611         },
6612
6613         /**
6614          * Selects the value of a node, optionally replacing null with the defaultValue.
6615          * @param {String} selector The selector/xpath query
6616          * @param {Node} root (optional) The start of the query (defaults to document).
6617          * @param {String} defaultValue
6618          */
6619         selectValue : function(path, root, defaultValue){
6620             path = path.replace(trimRe, "");
6621             if(!valueCache[path]){
6622                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6623             }
6624             var n = valueCache[path](root);
6625             n = n[0] ? n[0] : n;
6626             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6627             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6628         },
6629
6630         /**
6631          * Selects the value of a node, parsing integers and floats.
6632          * @param {String} selector The selector/xpath query
6633          * @param {Node} root (optional) The start of the query (defaults to document).
6634          * @param {Number} defaultValue
6635          * @return {Number}
6636          */
6637         selectNumber : function(path, root, defaultValue){
6638             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6639             return parseFloat(v);
6640         },
6641
6642         /**
6643          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6644          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6645          * @param {String} selector The simple selector to test
6646          * @return {Boolean}
6647          */
6648         is : function(el, ss){
6649             if(typeof el == "string"){
6650                 el = document.getElementById(el);
6651             }
6652             var isArray = (el instanceof Array);
6653             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6654             return isArray ? (result.length == el.length) : (result.length > 0);
6655         },
6656
6657         /**
6658          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6659          * @param {Array} el An array of elements to filter
6660          * @param {String} selector The simple selector to test
6661          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6662          * the selector instead of the ones that match
6663          * @return {Array}
6664          */
6665         filter : function(els, ss, nonMatches){
6666             ss = ss.replace(trimRe, "");
6667             if(!simpleCache[ss]){
6668                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6669             }
6670             var result = simpleCache[ss](els);
6671             return nonMatches ? quickDiff(result, els) : result;
6672         },
6673
6674         /**
6675          * Collection of matching regular expressions and code snippets.
6676          */
6677         matchers : [{
6678                 re: /^\.([\w-]+)/,
6679                 select: 'n = byClassName(n, null, " {1} ");'
6680             }, {
6681                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6682                 select: 'n = byPseudo(n, "{1}", "{2}");'
6683             },{
6684                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6685                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6686             }, {
6687                 re: /^#([\w-]+)/,
6688                 select: 'n = byId(n, null, "{1}");'
6689             },{
6690                 re: /^@([\w-]+)/,
6691                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6692             }
6693         ],
6694
6695         /**
6696          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6697          * 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;.
6698          */
6699         operators : {
6700             "=" : function(a, v){
6701                 return a == v;
6702             },
6703             "!=" : function(a, v){
6704                 return a != v;
6705             },
6706             "^=" : function(a, v){
6707                 return a && a.substr(0, v.length) == v;
6708             },
6709             "$=" : function(a, v){
6710                 return a && a.substr(a.length-v.length) == v;
6711             },
6712             "*=" : function(a, v){
6713                 return a && a.indexOf(v) !== -1;
6714             },
6715             "%=" : function(a, v){
6716                 return (a % v) == 0;
6717             },
6718             "|=" : function(a, v){
6719                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6720             },
6721             "~=" : function(a, v){
6722                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6723             }
6724         },
6725
6726         /**
6727          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6728          * and the argument (if any) supplied in the selector.
6729          */
6730         pseudos : {
6731             "first-child" : function(c){
6732                 var r = [], ri = -1, n;
6733                 for(var i = 0, ci; ci = n = c[i]; i++){
6734                     while((n = n.previousSibling) && n.nodeType != 1);
6735                     if(!n){
6736                         r[++ri] = ci;
6737                     }
6738                 }
6739                 return r;
6740             },
6741
6742             "last-child" : function(c){
6743                 var r = [], ri = -1, n;
6744                 for(var i = 0, ci; ci = n = c[i]; i++){
6745                     while((n = n.nextSibling) && n.nodeType != 1);
6746                     if(!n){
6747                         r[++ri] = ci;
6748                     }
6749                 }
6750                 return r;
6751             },
6752
6753             "nth-child" : function(c, a) {
6754                 var r = [], ri = -1;
6755                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6756                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6757                 for(var i = 0, n; n = c[i]; i++){
6758                     var pn = n.parentNode;
6759                     if (batch != pn._batch) {
6760                         var j = 0;
6761                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6762                             if(cn.nodeType == 1){
6763                                cn.nodeIndex = ++j;
6764                             }
6765                         }
6766                         pn._batch = batch;
6767                     }
6768                     if (f == 1) {
6769                         if (l == 0 || n.nodeIndex == l){
6770                             r[++ri] = n;
6771                         }
6772                     } else if ((n.nodeIndex + l) % f == 0){
6773                         r[++ri] = n;
6774                     }
6775                 }
6776
6777                 return r;
6778             },
6779
6780             "only-child" : function(c){
6781                 var r = [], ri = -1;;
6782                 for(var i = 0, ci; ci = c[i]; i++){
6783                     if(!prev(ci) && !next(ci)){
6784                         r[++ri] = ci;
6785                     }
6786                 }
6787                 return r;
6788             },
6789
6790             "empty" : function(c){
6791                 var r = [], ri = -1;
6792                 for(var i = 0, ci; ci = c[i]; i++){
6793                     var cns = ci.childNodes, j = 0, cn, empty = true;
6794                     while(cn = cns[j]){
6795                         ++j;
6796                         if(cn.nodeType == 1 || cn.nodeType == 3){
6797                             empty = false;
6798                             break;
6799                         }
6800                     }
6801                     if(empty){
6802                         r[++ri] = ci;
6803                     }
6804                 }
6805                 return r;
6806             },
6807
6808             "contains" : function(c, v){
6809                 var r = [], ri = -1;
6810                 for(var i = 0, ci; ci = c[i]; i++){
6811                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6812                         r[++ri] = ci;
6813                     }
6814                 }
6815                 return r;
6816             },
6817
6818             "nodeValue" : function(c, v){
6819                 var r = [], ri = -1;
6820                 for(var i = 0, ci; ci = c[i]; i++){
6821                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6822                         r[++ri] = ci;
6823                     }
6824                 }
6825                 return r;
6826             },
6827
6828             "checked" : function(c){
6829                 var r = [], ri = -1;
6830                 for(var i = 0, ci; ci = c[i]; i++){
6831                     if(ci.checked == true){
6832                         r[++ri] = ci;
6833                     }
6834                 }
6835                 return r;
6836             },
6837
6838             "not" : function(c, ss){
6839                 return Roo.DomQuery.filter(c, ss, true);
6840             },
6841
6842             "odd" : function(c){
6843                 return this["nth-child"](c, "odd");
6844             },
6845
6846             "even" : function(c){
6847                 return this["nth-child"](c, "even");
6848             },
6849
6850             "nth" : function(c, a){
6851                 return c[a-1] || [];
6852             },
6853
6854             "first" : function(c){
6855                 return c[0] || [];
6856             },
6857
6858             "last" : function(c){
6859                 return c[c.length-1] || [];
6860             },
6861
6862             "has" : function(c, ss){
6863                 var s = Roo.DomQuery.select;
6864                 var r = [], ri = -1;
6865                 for(var i = 0, ci; ci = c[i]; i++){
6866                     if(s(ss, ci).length > 0){
6867                         r[++ri] = ci;
6868                     }
6869                 }
6870                 return r;
6871             },
6872
6873             "next" : function(c, ss){
6874                 var is = Roo.DomQuery.is;
6875                 var r = [], ri = -1;
6876                 for(var i = 0, ci; ci = c[i]; i++){
6877                     var n = next(ci);
6878                     if(n && is(n, ss)){
6879                         r[++ri] = ci;
6880                     }
6881                 }
6882                 return r;
6883             },
6884
6885             "prev" : function(c, ss){
6886                 var is = Roo.DomQuery.is;
6887                 var r = [], ri = -1;
6888                 for(var i = 0, ci; ci = c[i]; i++){
6889                     var n = prev(ci);
6890                     if(n && is(n, ss)){
6891                         r[++ri] = ci;
6892                     }
6893                 }
6894                 return r;
6895             }
6896         }
6897     };
6898 }();
6899
6900 /**
6901  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6902  * @param {String} path The selector/xpath query
6903  * @param {Node} root (optional) The start of the query (defaults to document).
6904  * @return {Array}
6905  * @member Roo
6906  * @method query
6907  */
6908 Roo.query = Roo.DomQuery.select;
6909 /*
6910  * Based on:
6911  * Ext JS Library 1.1.1
6912  * Copyright(c) 2006-2007, Ext JS, LLC.
6913  *
6914  * Originally Released Under LGPL - original licence link has changed is not relivant.
6915  *
6916  * Fork - LGPL
6917  * <script type="text/javascript">
6918  */
6919
6920 /**
6921  * @class Roo.util.Observable
6922  * Base class that provides a common interface for publishing events. Subclasses are expected to
6923  * to have a property "events" with all the events defined.<br>
6924  * For example:
6925  * <pre><code>
6926  Employee = function(name){
6927     this.name = name;
6928     this.addEvents({
6929         "fired" : true,
6930         "quit" : true
6931     });
6932  }
6933  Roo.extend(Employee, Roo.util.Observable);
6934 </code></pre>
6935  * @param {Object} config properties to use (incuding events / listeners)
6936  */
6937
6938 Roo.util.Observable = function(cfg){
6939     
6940     cfg = cfg|| {};
6941     this.addEvents(cfg.events || {});
6942     if (cfg.events) {
6943         delete cfg.events; // make sure
6944     }
6945      
6946     Roo.apply(this, cfg);
6947     
6948     if(this.listeners){
6949         this.on(this.listeners);
6950         delete this.listeners;
6951     }
6952 };
6953 Roo.util.Observable.prototype = {
6954     /** 
6955  * @cfg {Object} listeners  list of events and functions to call for this object, 
6956  * For example :
6957  * <pre><code>
6958     listeners :  { 
6959        'click' : function(e) {
6960            ..... 
6961         } ,
6962         .... 
6963     } 
6964   </code></pre>
6965  */
6966     
6967     
6968     /**
6969      * Fires the specified event with the passed parameters (minus the event name).
6970      * @param {String} eventName
6971      * @param {Object...} args Variable number of parameters are passed to handlers
6972      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6973      */
6974     fireEvent : function(){
6975         var ce = this.events[arguments[0].toLowerCase()];
6976         if(typeof ce == "object"){
6977             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6978         }else{
6979             return true;
6980         }
6981     },
6982
6983     // private
6984     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6985
6986     /**
6987      * Appends an event handler to this component
6988      * @param {String}   eventName The type of event to listen for
6989      * @param {Function} handler The method the event invokes
6990      * @param {Object}   scope (optional) The scope in which to execute the handler
6991      * function. The handler function's "this" context.
6992      * @param {Object}   options (optional) An object containing handler configuration
6993      * properties. This may contain any of the following properties:<ul>
6994      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6995      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6996      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6997      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6998      * by the specified number of milliseconds. If the event fires again within that time, the original
6999      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7000      * </ul><br>
7001      * <p>
7002      * <b>Combining Options</b><br>
7003      * Using the options argument, it is possible to combine different types of listeners:<br>
7004      * <br>
7005      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
7006                 <pre><code>
7007                 el.on('click', this.onClick, this, {
7008                         single: true,
7009                 delay: 100,
7010                 forumId: 4
7011                 });
7012                 </code></pre>
7013      * <p>
7014      * <b>Attaching multiple handlers in 1 call</b><br>
7015      * The method also allows for a single argument to be passed which is a config object containing properties
7016      * which specify multiple handlers.
7017      * <pre><code>
7018                 el.on({
7019                         'click': {
7020                         fn: this.onClick,
7021                         scope: this,
7022                         delay: 100
7023                 }, 
7024                 'mouseover': {
7025                         fn: this.onMouseOver,
7026                         scope: this
7027                 },
7028                 'mouseout': {
7029                         fn: this.onMouseOut,
7030                         scope: this
7031                 }
7032                 });
7033                 </code></pre>
7034      * <p>
7035      * Or a shorthand syntax which passes the same scope object to all handlers:
7036         <pre><code>
7037                 el.on({
7038                         'click': this.onClick,
7039                 'mouseover': this.onMouseOver,
7040                 'mouseout': this.onMouseOut,
7041                 scope: this
7042                 });
7043                 </code></pre>
7044      */
7045     addListener : function(eventName, fn, scope, o){
7046         if(typeof eventName == "object"){
7047             o = eventName;
7048             for(var e in o){
7049                 if(this.filterOptRe.test(e)){
7050                     continue;
7051                 }
7052                 if(typeof o[e] == "function"){
7053                     // shared options
7054                     this.addListener(e, o[e], o.scope,  o);
7055                 }else{
7056                     // individual options
7057                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7058                 }
7059             }
7060             return;
7061         }
7062         o = (!o || typeof o == "boolean") ? {} : o;
7063         eventName = eventName.toLowerCase();
7064         var ce = this.events[eventName] || true;
7065         if(typeof ce == "boolean"){
7066             ce = new Roo.util.Event(this, eventName);
7067             this.events[eventName] = ce;
7068         }
7069         ce.addListener(fn, scope, o);
7070     },
7071
7072     /**
7073      * Removes a listener
7074      * @param {String}   eventName     The type of event to listen for
7075      * @param {Function} handler        The handler to remove
7076      * @param {Object}   scope  (optional) The scope (this object) for the handler
7077      */
7078     removeListener : function(eventName, fn, scope){
7079         var ce = this.events[eventName.toLowerCase()];
7080         if(typeof ce == "object"){
7081             ce.removeListener(fn, scope);
7082         }
7083     },
7084
7085     /**
7086      * Removes all listeners for this object
7087      */
7088     purgeListeners : function(){
7089         for(var evt in this.events){
7090             if(typeof this.events[evt] == "object"){
7091                  this.events[evt].clearListeners();
7092             }
7093         }
7094     },
7095
7096     relayEvents : function(o, events){
7097         var createHandler = function(ename){
7098             return function(){
7099                  
7100                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7101             };
7102         };
7103         for(var i = 0, len = events.length; i < len; i++){
7104             var ename = events[i];
7105             if(!this.events[ename]){
7106                 this.events[ename] = true;
7107             };
7108             o.on(ename, createHandler(ename), this);
7109         }
7110     },
7111
7112     /**
7113      * Used to define events on this Observable
7114      * @param {Object} object The object with the events defined
7115      */
7116     addEvents : function(o){
7117         if(!this.events){
7118             this.events = {};
7119         }
7120         Roo.applyIf(this.events, o);
7121     },
7122
7123     /**
7124      * Checks to see if this object has any listeners for a specified event
7125      * @param {String} eventName The name of the event to check for
7126      * @return {Boolean} True if the event is being listened for, else false
7127      */
7128     hasListener : function(eventName){
7129         var e = this.events[eventName];
7130         return typeof e == "object" && e.listeners.length > 0;
7131     }
7132 };
7133 /**
7134  * Appends an event handler to this element (shorthand for addListener)
7135  * @param {String}   eventName     The type of event to listen for
7136  * @param {Function} handler        The method the event invokes
7137  * @param {Object}   scope (optional) The scope in which to execute the handler
7138  * function. The handler function's "this" context.
7139  * @param {Object}   options  (optional)
7140  * @method
7141  */
7142 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7143 /**
7144  * Removes a listener (shorthand for removeListener)
7145  * @param {String}   eventName     The type of event to listen for
7146  * @param {Function} handler        The handler to remove
7147  * @param {Object}   scope  (optional) The scope (this object) for the handler
7148  * @method
7149  */
7150 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7151
7152 /**
7153  * Starts capture on the specified Observable. All events will be passed
7154  * to the supplied function with the event name + standard signature of the event
7155  * <b>before</b> the event is fired. If the supplied function returns false,
7156  * the event will not fire.
7157  * @param {Observable} o The Observable to capture
7158  * @param {Function} fn The function to call
7159  * @param {Object} scope (optional) The scope (this object) for the fn
7160  * @static
7161  */
7162 Roo.util.Observable.capture = function(o, fn, scope){
7163     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7164 };
7165
7166 /**
7167  * Removes <b>all</b> added captures from the Observable.
7168  * @param {Observable} o The Observable to release
7169  * @static
7170  */
7171 Roo.util.Observable.releaseCapture = function(o){
7172     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7173 };
7174
7175 (function(){
7176
7177     var createBuffered = function(h, o, scope){
7178         var task = new Roo.util.DelayedTask();
7179         return function(){
7180             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7181         };
7182     };
7183
7184     var createSingle = function(h, e, fn, scope){
7185         return function(){
7186             e.removeListener(fn, scope);
7187             return h.apply(scope, arguments);
7188         };
7189     };
7190
7191     var createDelayed = function(h, o, scope){
7192         return function(){
7193             var args = Array.prototype.slice.call(arguments, 0);
7194             setTimeout(function(){
7195                 h.apply(scope, args);
7196             }, o.delay || 10);
7197         };
7198     };
7199
7200     Roo.util.Event = function(obj, name){
7201         this.name = name;
7202         this.obj = obj;
7203         this.listeners = [];
7204     };
7205
7206     Roo.util.Event.prototype = {
7207         addListener : function(fn, scope, options){
7208             var o = options || {};
7209             scope = scope || this.obj;
7210             if(!this.isListening(fn, scope)){
7211                 var l = {fn: fn, scope: scope, options: o};
7212                 var h = fn;
7213                 if(o.delay){
7214                     h = createDelayed(h, o, scope);
7215                 }
7216                 if(o.single){
7217                     h = createSingle(h, this, fn, scope);
7218                 }
7219                 if(o.buffer){
7220                     h = createBuffered(h, o, scope);
7221                 }
7222                 l.fireFn = h;
7223                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7224                     this.listeners.push(l);
7225                 }else{
7226                     this.listeners = this.listeners.slice(0);
7227                     this.listeners.push(l);
7228                 }
7229             }
7230         },
7231
7232         findListener : function(fn, scope){
7233             scope = scope || this.obj;
7234             var ls = this.listeners;
7235             for(var i = 0, len = ls.length; i < len; i++){
7236                 var l = ls[i];
7237                 if(l.fn == fn && l.scope == scope){
7238                     return i;
7239                 }
7240             }
7241             return -1;
7242         },
7243
7244         isListening : function(fn, scope){
7245             return this.findListener(fn, scope) != -1;
7246         },
7247
7248         removeListener : function(fn, scope){
7249             var index;
7250             if((index = this.findListener(fn, scope)) != -1){
7251                 if(!this.firing){
7252                     this.listeners.splice(index, 1);
7253                 }else{
7254                     this.listeners = this.listeners.slice(0);
7255                     this.listeners.splice(index, 1);
7256                 }
7257                 return true;
7258             }
7259             return false;
7260         },
7261
7262         clearListeners : function(){
7263             this.listeners = [];
7264         },
7265
7266         fire : function(){
7267             var ls = this.listeners, scope, len = ls.length;
7268             if(len > 0){
7269                 this.firing = true;
7270                 var args = Array.prototype.slice.call(arguments, 0);                
7271                 for(var i = 0; i < len; i++){
7272                     var l = ls[i];
7273                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7274                         this.firing = false;
7275                         return false;
7276                     }
7277                 }
7278                 this.firing = false;
7279             }
7280             return true;
7281         }
7282     };
7283 })();/*
7284  * RooJS Library 
7285  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7286  *
7287  * Licence LGPL 
7288  *
7289  */
7290  
7291 /**
7292  * @class Roo.Document
7293  * @extends Roo.util.Observable
7294  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7295  * 
7296  * @param {Object} config the methods and properties of the 'base' class for the application.
7297  * 
7298  *  Generic Page handler - implement this to start your app..
7299  * 
7300  * eg.
7301  *  MyProject = new Roo.Document({
7302         events : {
7303             'load' : true // your events..
7304         },
7305         listeners : {
7306             'ready' : function() {
7307                 // fired on Roo.onReady()
7308             }
7309         }
7310  * 
7311  */
7312 Roo.Document = function(cfg) {
7313      
7314     this.addEvents({ 
7315         'ready' : true
7316     });
7317     Roo.util.Observable.call(this,cfg);
7318     
7319     var _this = this;
7320     
7321     Roo.onReady(function() {
7322         _this.fireEvent('ready');
7323     },null,false);
7324     
7325     
7326 }
7327
7328 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7329  * Based on:
7330  * Ext JS Library 1.1.1
7331  * Copyright(c) 2006-2007, Ext JS, LLC.
7332  *
7333  * Originally Released Under LGPL - original licence link has changed is not relivant.
7334  *
7335  * Fork - LGPL
7336  * <script type="text/javascript">
7337  */
7338
7339 /**
7340  * @class Roo.EventManager
7341  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7342  * several useful events directly.
7343  * See {@link Roo.EventObject} for more details on normalized event objects.
7344  * @static
7345  */
7346 Roo.EventManager = function(){
7347     var docReadyEvent, docReadyProcId, docReadyState = false;
7348     var resizeEvent, resizeTask, textEvent, textSize;
7349     var E = Roo.lib.Event;
7350     var D = Roo.lib.Dom;
7351
7352     
7353     
7354
7355     var fireDocReady = function(){
7356         if(!docReadyState){
7357             docReadyState = true;
7358             Roo.isReady = true;
7359             if(docReadyProcId){
7360                 clearInterval(docReadyProcId);
7361             }
7362             if(Roo.isGecko || Roo.isOpera) {
7363                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7364             }
7365             if(Roo.isIE){
7366                 var defer = document.getElementById("ie-deferred-loader");
7367                 if(defer){
7368                     defer.onreadystatechange = null;
7369                     defer.parentNode.removeChild(defer);
7370                 }
7371             }
7372             if(docReadyEvent){
7373                 docReadyEvent.fire();
7374                 docReadyEvent.clearListeners();
7375             }
7376         }
7377     };
7378     
7379     var initDocReady = function(){
7380         docReadyEvent = new Roo.util.Event();
7381         if(Roo.isGecko || Roo.isOpera) {
7382             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7383         }else if(Roo.isIE){
7384             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7385             var defer = document.getElementById("ie-deferred-loader");
7386             defer.onreadystatechange = function(){
7387                 if(this.readyState == "complete"){
7388                     fireDocReady();
7389                 }
7390             };
7391         }else if(Roo.isSafari){ 
7392             docReadyProcId = setInterval(function(){
7393                 var rs = document.readyState;
7394                 if(rs == "complete") {
7395                     fireDocReady();     
7396                  }
7397             }, 10);
7398         }
7399         // no matter what, make sure it fires on load
7400         E.on(window, "load", fireDocReady);
7401     };
7402
7403     var createBuffered = function(h, o){
7404         var task = new Roo.util.DelayedTask(h);
7405         return function(e){
7406             // create new event object impl so new events don't wipe out properties
7407             e = new Roo.EventObjectImpl(e);
7408             task.delay(o.buffer, h, null, [e]);
7409         };
7410     };
7411
7412     var createSingle = function(h, el, ename, fn){
7413         return function(e){
7414             Roo.EventManager.removeListener(el, ename, fn);
7415             h(e);
7416         };
7417     };
7418
7419     var createDelayed = function(h, o){
7420         return function(e){
7421             // create new event object impl so new events don't wipe out properties
7422             e = new Roo.EventObjectImpl(e);
7423             setTimeout(function(){
7424                 h(e);
7425             }, o.delay || 10);
7426         };
7427     };
7428     var transitionEndVal = false;
7429     
7430     var transitionEnd = function()
7431     {
7432         if (transitionEndVal) {
7433             return transitionEndVal;
7434         }
7435         var el = document.createElement('div');
7436
7437         var transEndEventNames = {
7438             WebkitTransition : 'webkitTransitionEnd',
7439             MozTransition    : 'transitionend',
7440             OTransition      : 'oTransitionEnd otransitionend',
7441             transition       : 'transitionend'
7442         };
7443     
7444         for (var name in transEndEventNames) {
7445             if (el.style[name] !== undefined) {
7446                 transitionEndVal = transEndEventNames[name];
7447                 return  transitionEndVal ;
7448             }
7449         }
7450     }
7451     
7452   
7453
7454     var listen = function(element, ename, opt, fn, scope)
7455     {
7456         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7457         fn = fn || o.fn; scope = scope || o.scope;
7458         var el = Roo.getDom(element);
7459         
7460         
7461         if(!el){
7462             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7463         }
7464         
7465         if (ename == 'transitionend') {
7466             ename = transitionEnd();
7467         }
7468         var h = function(e){
7469             e = Roo.EventObject.setEvent(e);
7470             var t;
7471             if(o.delegate){
7472                 t = e.getTarget(o.delegate, el);
7473                 if(!t){
7474                     return;
7475                 }
7476             }else{
7477                 t = e.target;
7478             }
7479             if(o.stopEvent === true){
7480                 e.stopEvent();
7481             }
7482             if(o.preventDefault === true){
7483                e.preventDefault();
7484             }
7485             if(o.stopPropagation === true){
7486                 e.stopPropagation();
7487             }
7488
7489             if(o.normalized === false){
7490                 e = e.browserEvent;
7491             }
7492
7493             fn.call(scope || el, e, t, o);
7494         };
7495         if(o.delay){
7496             h = createDelayed(h, o);
7497         }
7498         if(o.single){
7499             h = createSingle(h, el, ename, fn);
7500         }
7501         if(o.buffer){
7502             h = createBuffered(h, o);
7503         }
7504         
7505         fn._handlers = fn._handlers || [];
7506         
7507         
7508         fn._handlers.push([Roo.id(el), ename, h]);
7509         
7510         
7511          
7512         E.on(el, ename, h); // this adds the actuall listener to the object..
7513         
7514         
7515         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7516             el.addEventListener("DOMMouseScroll", h, false);
7517             E.on(window, 'unload', function(){
7518                 el.removeEventListener("DOMMouseScroll", h, false);
7519             });
7520         }
7521         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7522             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7523         }
7524         return h;
7525     };
7526
7527     var stopListening = function(el, ename, fn){
7528         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7529         if(hds){
7530             for(var i = 0, len = hds.length; i < len; i++){
7531                 var h = hds[i];
7532                 if(h[0] == id && h[1] == ename){
7533                     hd = h[2];
7534                     hds.splice(i, 1);
7535                     break;
7536                 }
7537             }
7538         }
7539         E.un(el, ename, hd);
7540         el = Roo.getDom(el);
7541         if(ename == "mousewheel" && el.addEventListener){
7542             el.removeEventListener("DOMMouseScroll", hd, false);
7543         }
7544         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7545             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7546         }
7547     };
7548
7549     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7550     
7551     var pub = {
7552         
7553         
7554         /** 
7555          * Fix for doc tools
7556          * @scope Roo.EventManager
7557          */
7558         
7559         
7560         /** 
7561          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7562          * object with a Roo.EventObject
7563          * @param {Function} fn        The method the event invokes
7564          * @param {Object}   scope    An object that becomes the scope of the handler
7565          * @param {boolean}  override If true, the obj passed in becomes
7566          *                             the execution scope of the listener
7567          * @return {Function} The wrapped function
7568          * @deprecated
7569          */
7570         wrap : function(fn, scope, override){
7571             return function(e){
7572                 Roo.EventObject.setEvent(e);
7573                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7574             };
7575         },
7576         
7577         /**
7578      * Appends an event handler to an element (shorthand for addListener)
7579      * @param {String/HTMLElement}   element        The html element or id to assign the
7580      * @param {String}   eventName The type of event to listen for
7581      * @param {Function} handler The method the event invokes
7582      * @param {Object}   scope (optional) The scope in which to execute the handler
7583      * function. The handler function's "this" context.
7584      * @param {Object}   options (optional) An object containing handler configuration
7585      * properties. This may contain any of the following properties:<ul>
7586      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7587      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7588      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7589      * <li>preventDefault {Boolean} True to prevent the default action</li>
7590      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7591      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7592      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7593      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7594      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7595      * by the specified number of milliseconds. If the event fires again within that time, the original
7596      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7597      * </ul><br>
7598      * <p>
7599      * <b>Combining Options</b><br>
7600      * Using the options argument, it is possible to combine different types of listeners:<br>
7601      * <br>
7602      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7603      * Code:<pre><code>
7604 el.on('click', this.onClick, this, {
7605     single: true,
7606     delay: 100,
7607     stopEvent : true,
7608     forumId: 4
7609 });</code></pre>
7610      * <p>
7611      * <b>Attaching multiple handlers in 1 call</b><br>
7612       * The method also allows for a single argument to be passed which is a config object containing properties
7613      * which specify multiple handlers.
7614      * <p>
7615      * Code:<pre><code>
7616 el.on({
7617     'click' : {
7618         fn: this.onClick
7619         scope: this,
7620         delay: 100
7621     },
7622     'mouseover' : {
7623         fn: this.onMouseOver
7624         scope: this
7625     },
7626     'mouseout' : {
7627         fn: this.onMouseOut
7628         scope: this
7629     }
7630 });</code></pre>
7631      * <p>
7632      * Or a shorthand syntax:<br>
7633      * Code:<pre><code>
7634 el.on({
7635     'click' : this.onClick,
7636     'mouseover' : this.onMouseOver,
7637     'mouseout' : this.onMouseOut
7638     scope: this
7639 });</code></pre>
7640      */
7641         addListener : function(element, eventName, fn, scope, options){
7642             if(typeof eventName == "object"){
7643                 var o = eventName;
7644                 for(var e in o){
7645                     if(propRe.test(e)){
7646                         continue;
7647                     }
7648                     if(typeof o[e] == "function"){
7649                         // shared options
7650                         listen(element, e, o, o[e], o.scope);
7651                     }else{
7652                         // individual options
7653                         listen(element, e, o[e]);
7654                     }
7655                 }
7656                 return;
7657             }
7658             return listen(element, eventName, options, fn, scope);
7659         },
7660         
7661         /**
7662          * Removes an event handler
7663          *
7664          * @param {String/HTMLElement}   element        The id or html element to remove the 
7665          *                             event from
7666          * @param {String}   eventName     The type of event
7667          * @param {Function} fn
7668          * @return {Boolean} True if a listener was actually removed
7669          */
7670         removeListener : function(element, eventName, fn){
7671             return stopListening(element, eventName, fn);
7672         },
7673         
7674         /**
7675          * Fires when the document is ready (before onload and before images are loaded). Can be 
7676          * accessed shorthanded Roo.onReady().
7677          * @param {Function} fn        The method the event invokes
7678          * @param {Object}   scope    An  object that becomes the scope of the handler
7679          * @param {boolean}  options
7680          */
7681         onDocumentReady : function(fn, scope, options){
7682             if(docReadyState){ // if it already fired
7683                 docReadyEvent.addListener(fn, scope, options);
7684                 docReadyEvent.fire();
7685                 docReadyEvent.clearListeners();
7686                 return;
7687             }
7688             if(!docReadyEvent){
7689                 initDocReady();
7690             }
7691             docReadyEvent.addListener(fn, scope, options);
7692         },
7693         
7694         /**
7695          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7696          * @param {Function} fn        The method the event invokes
7697          * @param {Object}   scope    An object that becomes the scope of the handler
7698          * @param {boolean}  options
7699          */
7700         onWindowResize : function(fn, scope, options)
7701         {
7702             if(!resizeEvent){
7703                 resizeEvent = new Roo.util.Event();
7704                 resizeTask = new Roo.util.DelayedTask(function(){
7705                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7706                 });
7707                 E.on(window, "resize", function()
7708                 {
7709                     if (Roo.isIE) {
7710                         resizeTask.delay(50);
7711                     } else {
7712                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7713                     }
7714                 });
7715             }
7716             resizeEvent.addListener(fn, scope, options);
7717         },
7718
7719         /**
7720          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7721          * @param {Function} fn        The method the event invokes
7722          * @param {Object}   scope    An object that becomes the scope of the handler
7723          * @param {boolean}  options
7724          */
7725         onTextResize : function(fn, scope, options){
7726             if(!textEvent){
7727                 textEvent = new Roo.util.Event();
7728                 var textEl = new Roo.Element(document.createElement('div'));
7729                 textEl.dom.className = 'x-text-resize';
7730                 textEl.dom.innerHTML = 'X';
7731                 textEl.appendTo(document.body);
7732                 textSize = textEl.dom.offsetHeight;
7733                 setInterval(function(){
7734                     if(textEl.dom.offsetHeight != textSize){
7735                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7736                     }
7737                 }, this.textResizeInterval);
7738             }
7739             textEvent.addListener(fn, scope, options);
7740         },
7741
7742         /**
7743          * Removes the passed window resize listener.
7744          * @param {Function} fn        The method the event invokes
7745          * @param {Object}   scope    The scope of handler
7746          */
7747         removeResizeListener : function(fn, scope){
7748             if(resizeEvent){
7749                 resizeEvent.removeListener(fn, scope);
7750             }
7751         },
7752
7753         // private
7754         fireResize : function(){
7755             if(resizeEvent){
7756                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7757             }   
7758         },
7759         /**
7760          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7761          */
7762         ieDeferSrc : false,
7763         /**
7764          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7765          */
7766         textResizeInterval : 50
7767     };
7768     
7769     /**
7770      * Fix for doc tools
7771      * @scopeAlias pub=Roo.EventManager
7772      */
7773     
7774      /**
7775      * Appends an event handler to an element (shorthand for addListener)
7776      * @param {String/HTMLElement}   element        The html element or id to assign the
7777      * @param {String}   eventName The type of event to listen for
7778      * @param {Function} handler The method the event invokes
7779      * @param {Object}   scope (optional) The scope in which to execute the handler
7780      * function. The handler function's "this" context.
7781      * @param {Object}   options (optional) An object containing handler configuration
7782      * properties. This may contain any of the following properties:<ul>
7783      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7784      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7785      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7786      * <li>preventDefault {Boolean} True to prevent the default action</li>
7787      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7788      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7789      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7790      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7791      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7792      * by the specified number of milliseconds. If the event fires again within that time, the original
7793      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7794      * </ul><br>
7795      * <p>
7796      * <b>Combining Options</b><br>
7797      * Using the options argument, it is possible to combine different types of listeners:<br>
7798      * <br>
7799      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7800      * Code:<pre><code>
7801 el.on('click', this.onClick, this, {
7802     single: true,
7803     delay: 100,
7804     stopEvent : true,
7805     forumId: 4
7806 });</code></pre>
7807      * <p>
7808      * <b>Attaching multiple handlers in 1 call</b><br>
7809       * The method also allows for a single argument to be passed which is a config object containing properties
7810      * which specify multiple handlers.
7811      * <p>
7812      * Code:<pre><code>
7813 el.on({
7814     'click' : {
7815         fn: this.onClick
7816         scope: this,
7817         delay: 100
7818     },
7819     'mouseover' : {
7820         fn: this.onMouseOver
7821         scope: this
7822     },
7823     'mouseout' : {
7824         fn: this.onMouseOut
7825         scope: this
7826     }
7827 });</code></pre>
7828      * <p>
7829      * Or a shorthand syntax:<br>
7830      * Code:<pre><code>
7831 el.on({
7832     'click' : this.onClick,
7833     'mouseover' : this.onMouseOver,
7834     'mouseout' : this.onMouseOut
7835     scope: this
7836 });</code></pre>
7837      */
7838     pub.on = pub.addListener;
7839     pub.un = pub.removeListener;
7840
7841     pub.stoppedMouseDownEvent = new Roo.util.Event();
7842     return pub;
7843 }();
7844 /**
7845   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7846   * @param {Function} fn        The method the event invokes
7847   * @param {Object}   scope    An  object that becomes the scope of the handler
7848   * @param {boolean}  override If true, the obj passed in becomes
7849   *                             the execution scope of the listener
7850   * @member Roo
7851   * @method onReady
7852  */
7853 Roo.onReady = Roo.EventManager.onDocumentReady;
7854
7855 Roo.onReady(function(){
7856     var bd = Roo.get(document.body);
7857     if(!bd){ return; }
7858
7859     var cls = [
7860             Roo.isIE ? "roo-ie"
7861             : Roo.isIE11 ? "roo-ie11"
7862             : Roo.isEdge ? "roo-edge"
7863             : Roo.isGecko ? "roo-gecko"
7864             : Roo.isOpera ? "roo-opera"
7865             : Roo.isSafari ? "roo-safari" : ""];
7866
7867     if(Roo.isMac){
7868         cls.push("roo-mac");
7869     }
7870     if(Roo.isLinux){
7871         cls.push("roo-linux");
7872     }
7873     if(Roo.isIOS){
7874         cls.push("roo-ios");
7875     }
7876     if(Roo.isTouch){
7877         cls.push("roo-touch");
7878     }
7879     if(Roo.isBorderBox){
7880         cls.push('roo-border-box');
7881     }
7882     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7883         var p = bd.dom.parentNode;
7884         if(p){
7885             p.className += ' roo-strict';
7886         }
7887     }
7888     bd.addClass(cls.join(' '));
7889 });
7890
7891 /**
7892  * @class Roo.EventObject
7893  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7894  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7895  * Example:
7896  * <pre><code>
7897  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7898     e.preventDefault();
7899     var target = e.getTarget();
7900     ...
7901  }
7902  var myDiv = Roo.get("myDiv");
7903  myDiv.on("click", handleClick);
7904  //or
7905  Roo.EventManager.on("myDiv", 'click', handleClick);
7906  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7907  </code></pre>
7908  * @static
7909  */
7910 Roo.EventObject = function(){
7911     
7912     var E = Roo.lib.Event;
7913     
7914     // safari keypress events for special keys return bad keycodes
7915     var safariKeys = {
7916         63234 : 37, // left
7917         63235 : 39, // right
7918         63232 : 38, // up
7919         63233 : 40, // down
7920         63276 : 33, // page up
7921         63277 : 34, // page down
7922         63272 : 46, // delete
7923         63273 : 36, // home
7924         63275 : 35  // end
7925     };
7926
7927     // normalize button clicks
7928     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7929                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7930
7931     Roo.EventObjectImpl = function(e){
7932         if(e){
7933             this.setEvent(e.browserEvent || e);
7934         }
7935     };
7936     Roo.EventObjectImpl.prototype = {
7937         /**
7938          * Used to fix doc tools.
7939          * @scope Roo.EventObject.prototype
7940          */
7941             
7942
7943         
7944         
7945         /** The normal browser event */
7946         browserEvent : null,
7947         /** The button pressed in a mouse event */
7948         button : -1,
7949         /** True if the shift key was down during the event */
7950         shiftKey : false,
7951         /** True if the control key was down during the event */
7952         ctrlKey : false,
7953         /** True if the alt key was down during the event */
7954         altKey : false,
7955
7956         /** Key constant 
7957         * @type Number */
7958         BACKSPACE : 8,
7959         /** Key constant 
7960         * @type Number */
7961         TAB : 9,
7962         /** Key constant 
7963         * @type Number */
7964         RETURN : 13,
7965         /** Key constant 
7966         * @type Number */
7967         ENTER : 13,
7968         /** Key constant 
7969         * @type Number */
7970         SHIFT : 16,
7971         /** Key constant 
7972         * @type Number */
7973         CONTROL : 17,
7974         /** Key constant 
7975         * @type Number */
7976         ESC : 27,
7977         /** Key constant 
7978         * @type Number */
7979         SPACE : 32,
7980         /** Key constant 
7981         * @type Number */
7982         PAGEUP : 33,
7983         /** Key constant 
7984         * @type Number */
7985         PAGEDOWN : 34,
7986         /** Key constant 
7987         * @type Number */
7988         END : 35,
7989         /** Key constant 
7990         * @type Number */
7991         HOME : 36,
7992         /** Key constant 
7993         * @type Number */
7994         LEFT : 37,
7995         /** Key constant 
7996         * @type Number */
7997         UP : 38,
7998         /** Key constant 
7999         * @type Number */
8000         RIGHT : 39,
8001         /** Key constant 
8002         * @type Number */
8003         DOWN : 40,
8004         /** Key constant 
8005         * @type Number */
8006         DELETE : 46,
8007         /** Key constant 
8008         * @type Number */
8009         F5 : 116,
8010
8011            /** @private */
8012         setEvent : function(e){
8013             if(e == this || (e && e.browserEvent)){ // already wrapped
8014                 return e;
8015             }
8016             this.browserEvent = e;
8017             if(e){
8018                 // normalize buttons
8019                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8020                 if(e.type == 'click' && this.button == -1){
8021                     this.button = 0;
8022                 }
8023                 this.type = e.type;
8024                 this.shiftKey = e.shiftKey;
8025                 // mac metaKey behaves like ctrlKey
8026                 this.ctrlKey = e.ctrlKey || e.metaKey;
8027                 this.altKey = e.altKey;
8028                 // in getKey these will be normalized for the mac
8029                 this.keyCode = e.keyCode;
8030                 // keyup warnings on firefox.
8031                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8032                 // cache the target for the delayed and or buffered events
8033                 this.target = E.getTarget(e);
8034                 // same for XY
8035                 this.xy = E.getXY(e);
8036             }else{
8037                 this.button = -1;
8038                 this.shiftKey = false;
8039                 this.ctrlKey = false;
8040                 this.altKey = false;
8041                 this.keyCode = 0;
8042                 this.charCode =0;
8043                 this.target = null;
8044                 this.xy = [0, 0];
8045             }
8046             return this;
8047         },
8048
8049         /**
8050          * Stop the event (preventDefault and stopPropagation)
8051          */
8052         stopEvent : function(){
8053             if(this.browserEvent){
8054                 if(this.browserEvent.type == 'mousedown'){
8055                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8056                 }
8057                 E.stopEvent(this.browserEvent);
8058             }
8059         },
8060
8061         /**
8062          * Prevents the browsers default handling of the event.
8063          */
8064         preventDefault : function(){
8065             if(this.browserEvent){
8066                 E.preventDefault(this.browserEvent);
8067             }
8068         },
8069
8070         /** @private */
8071         isNavKeyPress : function(){
8072             var k = this.keyCode;
8073             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8074             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8075         },
8076
8077         isSpecialKey : function(){
8078             var k = this.keyCode;
8079             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8080             (k == 16) || (k == 17) ||
8081             (k >= 18 && k <= 20) ||
8082             (k >= 33 && k <= 35) ||
8083             (k >= 36 && k <= 39) ||
8084             (k >= 44 && k <= 45);
8085         },
8086         /**
8087          * Cancels bubbling of the event.
8088          */
8089         stopPropagation : function(){
8090             if(this.browserEvent){
8091                 if(this.type == 'mousedown'){
8092                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8093                 }
8094                 E.stopPropagation(this.browserEvent);
8095             }
8096         },
8097
8098         /**
8099          * Gets the key code for the event.
8100          * @return {Number}
8101          */
8102         getCharCode : function(){
8103             return this.charCode || this.keyCode;
8104         },
8105
8106         /**
8107          * Returns a normalized keyCode for the event.
8108          * @return {Number} The key code
8109          */
8110         getKey : function(){
8111             var k = this.keyCode || this.charCode;
8112             return Roo.isSafari ? (safariKeys[k] || k) : k;
8113         },
8114
8115         /**
8116          * Gets the x coordinate of the event.
8117          * @return {Number}
8118          */
8119         getPageX : function(){
8120             return this.xy[0];
8121         },
8122
8123         /**
8124          * Gets the y coordinate of the event.
8125          * @return {Number}
8126          */
8127         getPageY : function(){
8128             return this.xy[1];
8129         },
8130
8131         /**
8132          * Gets the time of the event.
8133          * @return {Number}
8134          */
8135         getTime : function(){
8136             if(this.browserEvent){
8137                 return E.getTime(this.browserEvent);
8138             }
8139             return null;
8140         },
8141
8142         /**
8143          * Gets the page coordinates of the event.
8144          * @return {Array} The xy values like [x, y]
8145          */
8146         getXY : function(){
8147             return this.xy;
8148         },
8149
8150         /**
8151          * Gets the target for the event.
8152          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8153          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8154                 search as a number or element (defaults to 10 || document.body)
8155          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8156          * @return {HTMLelement}
8157          */
8158         getTarget : function(selector, maxDepth, returnEl){
8159             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8160         },
8161         /**
8162          * Gets the related target.
8163          * @return {HTMLElement}
8164          */
8165         getRelatedTarget : function(){
8166             if(this.browserEvent){
8167                 return E.getRelatedTarget(this.browserEvent);
8168             }
8169             return null;
8170         },
8171
8172         /**
8173          * Normalizes mouse wheel delta across browsers
8174          * @return {Number} The delta
8175          */
8176         getWheelDelta : function(){
8177             var e = this.browserEvent;
8178             var delta = 0;
8179             if(e.wheelDelta){ /* IE/Opera. */
8180                 delta = e.wheelDelta/120;
8181             }else if(e.detail){ /* Mozilla case. */
8182                 delta = -e.detail/3;
8183             }
8184             return delta;
8185         },
8186
8187         /**
8188          * Returns true if the control, meta, shift or alt key was pressed during this event.
8189          * @return {Boolean}
8190          */
8191         hasModifier : function(){
8192             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8193         },
8194
8195         /**
8196          * Returns true if the target of this event equals el or is a child of el
8197          * @param {String/HTMLElement/Element} el
8198          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8199          * @return {Boolean}
8200          */
8201         within : function(el, related){
8202             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8203             return t && Roo.fly(el).contains(t);
8204         },
8205
8206         getPoint : function(){
8207             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8208         }
8209     };
8210
8211     return new Roo.EventObjectImpl();
8212 }();
8213             
8214     /*
8215  * Based on:
8216  * Ext JS Library 1.1.1
8217  * Copyright(c) 2006-2007, Ext JS, LLC.
8218  *
8219  * Originally Released Under LGPL - original licence link has changed is not relivant.
8220  *
8221  * Fork - LGPL
8222  * <script type="text/javascript">
8223  */
8224
8225  
8226 // was in Composite Element!??!?!
8227  
8228 (function(){
8229     var D = Roo.lib.Dom;
8230     var E = Roo.lib.Event;
8231     var A = Roo.lib.Anim;
8232
8233     // local style camelizing for speed
8234     var propCache = {};
8235     var camelRe = /(-[a-z])/gi;
8236     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8237     var view = document.defaultView;
8238
8239 /**
8240  * @class Roo.Element
8241  * Represents an Element in the DOM.<br><br>
8242  * Usage:<br>
8243 <pre><code>
8244 var el = Roo.get("my-div");
8245
8246 // or with getEl
8247 var el = getEl("my-div");
8248
8249 // or with a DOM element
8250 var el = Roo.get(myDivElement);
8251 </code></pre>
8252  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8253  * each call instead of constructing a new one.<br><br>
8254  * <b>Animations</b><br />
8255  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8256  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8257 <pre>
8258 Option    Default   Description
8259 --------- --------  ---------------------------------------------
8260 duration  .35       The duration of the animation in seconds
8261 easing    easeOut   The YUI easing method
8262 callback  none      A function to execute when the anim completes
8263 scope     this      The scope (this) of the callback function
8264 </pre>
8265 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8266 * manipulate the animation. Here's an example:
8267 <pre><code>
8268 var el = Roo.get("my-div");
8269
8270 // no animation
8271 el.setWidth(100);
8272
8273 // default animation
8274 el.setWidth(100, true);
8275
8276 // animation with some options set
8277 el.setWidth(100, {
8278     duration: 1,
8279     callback: this.foo,
8280     scope: this
8281 });
8282
8283 // using the "anim" property to get the Anim object
8284 var opt = {
8285     duration: 1,
8286     callback: this.foo,
8287     scope: this
8288 };
8289 el.setWidth(100, opt);
8290 ...
8291 if(opt.anim.isAnimated()){
8292     opt.anim.stop();
8293 }
8294 </code></pre>
8295 * <b> Composite (Collections of) Elements</b><br />
8296  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8297  * @constructor Create a new Element directly.
8298  * @param {String/HTMLElement} element
8299  * @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).
8300  */
8301     Roo.Element = function(element, forceNew)
8302     {
8303         var dom = typeof element == "string" ?
8304                 document.getElementById(element) : element;
8305         
8306         this.listeners = {};
8307         
8308         if(!dom){ // invalid id/element
8309             return null;
8310         }
8311         var id = dom.id;
8312         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8313             return Roo.Element.cache[id];
8314         }
8315
8316         /**
8317          * The DOM element
8318          * @type HTMLElement
8319          */
8320         this.dom = dom;
8321
8322         /**
8323          * The DOM element ID
8324          * @type String
8325          */
8326         this.id = id || Roo.id(dom);
8327         
8328         return this; // assumed for cctor?
8329     };
8330
8331     var El = Roo.Element;
8332
8333     El.prototype = {
8334         /**
8335          * The element's default display mode  (defaults to "") 
8336          * @type String
8337          */
8338         originalDisplay : "",
8339
8340         
8341         // note this is overridden in BS version..
8342         visibilityMode : 1, 
8343         /**
8344          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8345          * @type String
8346          */
8347         defaultUnit : "px",
8348         
8349         /**
8350          * Sets the element's visibility mode. When setVisible() is called it
8351          * will use this to determine whether to set the visibility or the display property.
8352          * @param visMode Element.VISIBILITY or Element.DISPLAY
8353          * @return {Roo.Element} this
8354          */
8355         setVisibilityMode : function(visMode){
8356             this.visibilityMode = visMode;
8357             return this;
8358         },
8359         /**
8360          * Convenience method for setVisibilityMode(Element.DISPLAY)
8361          * @param {String} display (optional) What to set display to when visible
8362          * @return {Roo.Element} this
8363          */
8364         enableDisplayMode : function(display){
8365             this.setVisibilityMode(El.DISPLAY);
8366             if(typeof display != "undefined") { this.originalDisplay = display; }
8367             return this;
8368         },
8369
8370         /**
8371          * 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)
8372          * @param {String} selector The simple selector to test
8373          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8374                 search as a number or element (defaults to 10 || document.body)
8375          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8376          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8377          */
8378         findParent : function(simpleSelector, maxDepth, returnEl){
8379             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8380             maxDepth = maxDepth || 50;
8381             if(typeof maxDepth != "number"){
8382                 stopEl = Roo.getDom(maxDepth);
8383                 maxDepth = 10;
8384             }
8385             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8386                 if(dq.is(p, simpleSelector)){
8387                     return returnEl ? Roo.get(p) : p;
8388                 }
8389                 depth++;
8390                 p = p.parentNode;
8391             }
8392             return null;
8393         },
8394
8395
8396         /**
8397          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8398          * @param {String} selector The simple selector to test
8399          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8400                 search as a number or element (defaults to 10 || document.body)
8401          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8402          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8403          */
8404         findParentNode : function(simpleSelector, maxDepth, returnEl){
8405             var p = Roo.fly(this.dom.parentNode, '_internal');
8406             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8407         },
8408         
8409         /**
8410          * Looks at  the scrollable parent element
8411          */
8412         findScrollableParent : function()
8413         {
8414             var overflowRegex = /(auto|scroll)/;
8415             
8416             if(this.getStyle('position') === 'fixed'){
8417                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8418             }
8419             
8420             var excludeStaticParent = this.getStyle('position') === "absolute";
8421             
8422             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8423                 
8424                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8425                     continue;
8426                 }
8427                 
8428                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8429                     return parent;
8430                 }
8431                 
8432                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8433                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8434                 }
8435             }
8436             
8437             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8438         },
8439
8440         /**
8441          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8442          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8443          * @param {String} selector The simple selector to test
8444          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8445                 search as a number or element (defaults to 10 || document.body)
8446          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8447          */
8448         up : function(simpleSelector, maxDepth){
8449             return this.findParentNode(simpleSelector, maxDepth, true);
8450         },
8451
8452
8453
8454         /**
8455          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8456          * @param {String} selector The simple selector to test
8457          * @return {Boolean} True if this element matches the selector, else false
8458          */
8459         is : function(simpleSelector){
8460             return Roo.DomQuery.is(this.dom, simpleSelector);
8461         },
8462
8463         /**
8464          * Perform animation on this element.
8465          * @param {Object} args The YUI animation control args
8466          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8467          * @param {Function} onComplete (optional) Function to call when animation completes
8468          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8469          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8470          * @return {Roo.Element} this
8471          */
8472         animate : function(args, duration, onComplete, easing, animType){
8473             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8474             return this;
8475         },
8476
8477         /*
8478          * @private Internal animation call
8479          */
8480         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8481             animType = animType || 'run';
8482             opt = opt || {};
8483             var anim = Roo.lib.Anim[animType](
8484                 this.dom, args,
8485                 (opt.duration || defaultDur) || .35,
8486                 (opt.easing || defaultEase) || 'easeOut',
8487                 function(){
8488                     Roo.callback(cb, this);
8489                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8490                 },
8491                 this
8492             );
8493             opt.anim = anim;
8494             return anim;
8495         },
8496
8497         // private legacy anim prep
8498         preanim : function(a, i){
8499             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8500         },
8501
8502         /**
8503          * Removes worthless text nodes
8504          * @param {Boolean} forceReclean (optional) By default the element
8505          * keeps track if it has been cleaned already so
8506          * you can call this over and over. However, if you update the element and
8507          * need to force a reclean, you can pass true.
8508          */
8509         clean : function(forceReclean){
8510             if(this.isCleaned && forceReclean !== true){
8511                 return this;
8512             }
8513             var ns = /\S/;
8514             var d = this.dom, n = d.firstChild, ni = -1;
8515             while(n){
8516                 var nx = n.nextSibling;
8517                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8518                     d.removeChild(n);
8519                 }else{
8520                     n.nodeIndex = ++ni;
8521                 }
8522                 n = nx;
8523             }
8524             this.isCleaned = true;
8525             return this;
8526         },
8527
8528         // private
8529         calcOffsetsTo : function(el){
8530             el = Roo.get(el);
8531             var d = el.dom;
8532             var restorePos = false;
8533             if(el.getStyle('position') == 'static'){
8534                 el.position('relative');
8535                 restorePos = true;
8536             }
8537             var x = 0, y =0;
8538             var op = this.dom;
8539             while(op && op != d && op.tagName != 'HTML'){
8540                 x+= op.offsetLeft;
8541                 y+= op.offsetTop;
8542                 op = op.offsetParent;
8543             }
8544             if(restorePos){
8545                 el.position('static');
8546             }
8547             return [x, y];
8548         },
8549
8550         /**
8551          * Scrolls this element into view within the passed container.
8552          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8553          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8554          * @return {Roo.Element} this
8555          */
8556         scrollIntoView : function(container, hscroll){
8557             var c = Roo.getDom(container) || document.body;
8558             var el = this.dom;
8559
8560             var o = this.calcOffsetsTo(c),
8561                 l = o[0],
8562                 t = o[1],
8563                 b = t+el.offsetHeight,
8564                 r = l+el.offsetWidth;
8565
8566             var ch = c.clientHeight;
8567             var ct = parseInt(c.scrollTop, 10);
8568             var cl = parseInt(c.scrollLeft, 10);
8569             var cb = ct + ch;
8570             var cr = cl + c.clientWidth;
8571
8572             if(t < ct){
8573                 c.scrollTop = t;
8574             }else if(b > cb){
8575                 c.scrollTop = b-ch;
8576             }
8577
8578             if(hscroll !== false){
8579                 if(l < cl){
8580                     c.scrollLeft = l;
8581                 }else if(r > cr){
8582                     c.scrollLeft = r-c.clientWidth;
8583                 }
8584             }
8585             return this;
8586         },
8587
8588         // private
8589         scrollChildIntoView : function(child, hscroll){
8590             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8591         },
8592
8593         /**
8594          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8595          * the new height may not be available immediately.
8596          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8597          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8598          * @param {Function} onComplete (optional) Function to call when animation completes
8599          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8600          * @return {Roo.Element} this
8601          */
8602         autoHeight : function(animate, duration, onComplete, easing){
8603             var oldHeight = this.getHeight();
8604             this.clip();
8605             this.setHeight(1); // force clipping
8606             setTimeout(function(){
8607                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8608                 if(!animate){
8609                     this.setHeight(height);
8610                     this.unclip();
8611                     if(typeof onComplete == "function"){
8612                         onComplete();
8613                     }
8614                 }else{
8615                     this.setHeight(oldHeight); // restore original height
8616                     this.setHeight(height, animate, duration, function(){
8617                         this.unclip();
8618                         if(typeof onComplete == "function") { onComplete(); }
8619                     }.createDelegate(this), easing);
8620                 }
8621             }.createDelegate(this), 0);
8622             return this;
8623         },
8624
8625         /**
8626          * Returns true if this element is an ancestor of the passed element
8627          * @param {HTMLElement/String} el The element to check
8628          * @return {Boolean} True if this element is an ancestor of el, else false
8629          */
8630         contains : function(el){
8631             if(!el){return false;}
8632             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8633         },
8634
8635         /**
8636          * Checks whether the element is currently visible using both visibility and display properties.
8637          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8638          * @return {Boolean} True if the element is currently visible, else false
8639          */
8640         isVisible : function(deep) {
8641             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8642             if(deep !== true || !vis){
8643                 return vis;
8644             }
8645             var p = this.dom.parentNode;
8646             while(p && p.tagName.toLowerCase() != "body"){
8647                 if(!Roo.fly(p, '_isVisible').isVisible()){
8648                     return false;
8649                 }
8650                 p = p.parentNode;
8651             }
8652             return true;
8653         },
8654
8655         /**
8656          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8657          * @param {String} selector The CSS selector
8658          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8659          * @return {CompositeElement/CompositeElementLite} The composite element
8660          */
8661         select : function(selector, unique){
8662             return El.select(selector, unique, this.dom);
8663         },
8664
8665         /**
8666          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8667          * @param {String} selector The CSS selector
8668          * @return {Array} An array of the matched nodes
8669          */
8670         query : function(selector, unique){
8671             return Roo.DomQuery.select(selector, this.dom);
8672         },
8673
8674         /**
8675          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8676          * @param {String} selector The CSS selector
8677          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8678          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8679          */
8680         child : function(selector, returnDom){
8681             var n = Roo.DomQuery.selectNode(selector, this.dom);
8682             return returnDom ? n : Roo.get(n);
8683         },
8684
8685         /**
8686          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8687          * @param {String} selector The CSS selector
8688          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8689          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8690          */
8691         down : function(selector, returnDom){
8692             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8693             return returnDom ? n : Roo.get(n);
8694         },
8695
8696         /**
8697          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8698          * @param {String} group The group the DD object is member of
8699          * @param {Object} config The DD config object
8700          * @param {Object} overrides An object containing methods to override/implement on the DD object
8701          * @return {Roo.dd.DD} The DD object
8702          */
8703         initDD : function(group, config, overrides){
8704             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8705             return Roo.apply(dd, overrides);
8706         },
8707
8708         /**
8709          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8710          * @param {String} group The group the DDProxy object is member of
8711          * @param {Object} config The DDProxy config object
8712          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8713          * @return {Roo.dd.DDProxy} The DDProxy object
8714          */
8715         initDDProxy : function(group, config, overrides){
8716             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8717             return Roo.apply(dd, overrides);
8718         },
8719
8720         /**
8721          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8722          * @param {String} group The group the DDTarget object is member of
8723          * @param {Object} config The DDTarget config object
8724          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8725          * @return {Roo.dd.DDTarget} The DDTarget object
8726          */
8727         initDDTarget : function(group, config, overrides){
8728             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8729             return Roo.apply(dd, overrides);
8730         },
8731
8732         /**
8733          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8734          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8735          * @param {Boolean} visible Whether the element is visible
8736          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8737          * @return {Roo.Element} this
8738          */
8739          setVisible : function(visible, animate){
8740             if(!animate || !A){
8741                 if(this.visibilityMode == El.DISPLAY){
8742                     this.setDisplayed(visible);
8743                 }else{
8744                     this.fixDisplay();
8745                     this.dom.style.visibility = visible ? "visible" : "hidden";
8746                 }
8747             }else{
8748                 // closure for composites
8749                 var dom = this.dom;
8750                 var visMode = this.visibilityMode;
8751                 if(visible){
8752                     this.setOpacity(.01);
8753                     this.setVisible(true);
8754                 }
8755                 this.anim({opacity: { to: (visible?1:0) }},
8756                       this.preanim(arguments, 1),
8757                       null, .35, 'easeIn', function(){
8758                          if(!visible){
8759                              if(visMode == El.DISPLAY){
8760                                  dom.style.display = "none";
8761                              }else{
8762                                  dom.style.visibility = "hidden";
8763                              }
8764                              Roo.get(dom).setOpacity(1);
8765                          }
8766                      });
8767             }
8768             return this;
8769         },
8770
8771         /**
8772          * Returns true if display is not "none"
8773          * @return {Boolean}
8774          */
8775         isDisplayed : function() {
8776             return this.getStyle("display") != "none";
8777         },
8778
8779         /**
8780          * Toggles the element's visibility or display, depending on visibility mode.
8781          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8782          * @return {Roo.Element} this
8783          */
8784         toggle : function(animate){
8785             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8786             return this;
8787         },
8788
8789         /**
8790          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8791          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8792          * @return {Roo.Element} this
8793          */
8794         setDisplayed : function(value) {
8795             if(typeof value == "boolean"){
8796                value = value ? this.originalDisplay : "none";
8797             }
8798             this.setStyle("display", value);
8799             return this;
8800         },
8801
8802         /**
8803          * Tries to focus the element. Any exceptions are caught and ignored.
8804          * @return {Roo.Element} this
8805          */
8806         focus : function() {
8807             try{
8808                 this.dom.focus();
8809             }catch(e){}
8810             return this;
8811         },
8812
8813         /**
8814          * Tries to blur the element. Any exceptions are caught and ignored.
8815          * @return {Roo.Element} this
8816          */
8817         blur : function() {
8818             try{
8819                 this.dom.blur();
8820             }catch(e){}
8821             return this;
8822         },
8823
8824         /**
8825          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8826          * @param {String/Array} className The CSS class to add, or an array of classes
8827          * @return {Roo.Element} this
8828          */
8829         addClass : function(className){
8830             if(className instanceof Array){
8831                 for(var i = 0, len = className.length; i < len; i++) {
8832                     this.addClass(className[i]);
8833                 }
8834             }else{
8835                 if(className && !this.hasClass(className)){
8836                     if (this.dom instanceof SVGElement) {
8837                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8838                     } else {
8839                         this.dom.className = this.dom.className + " " + className;
8840                     }
8841                 }
8842             }
8843             return this;
8844         },
8845
8846         /**
8847          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8848          * @param {String/Array} className The CSS class to add, or an array of classes
8849          * @return {Roo.Element} this
8850          */
8851         radioClass : function(className){
8852             var siblings = this.dom.parentNode.childNodes;
8853             for(var i = 0; i < siblings.length; i++) {
8854                 var s = siblings[i];
8855                 if(s.nodeType == 1){
8856                     Roo.get(s).removeClass(className);
8857                 }
8858             }
8859             this.addClass(className);
8860             return this;
8861         },
8862
8863         /**
8864          * Removes one or more CSS classes from the element.
8865          * @param {String/Array} className The CSS class to remove, or an array of classes
8866          * @return {Roo.Element} this
8867          */
8868         removeClass : function(className){
8869             
8870             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8871             if(!className || !cn){
8872                 return this;
8873             }
8874             if(className instanceof Array){
8875                 for(var i = 0, len = className.length; i < len; i++) {
8876                     this.removeClass(className[i]);
8877                 }
8878             }else{
8879                 if(this.hasClass(className)){
8880                     var re = this.classReCache[className];
8881                     if (!re) {
8882                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8883                        this.classReCache[className] = re;
8884                     }
8885                     if (this.dom instanceof SVGElement) {
8886                         this.dom.className.baseVal = cn.replace(re, " ");
8887                     } else {
8888                         this.dom.className = cn.replace(re, " ");
8889                     }
8890                 }
8891             }
8892             return this;
8893         },
8894
8895         // private
8896         classReCache: {},
8897
8898         /**
8899          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8900          * @param {String} className The CSS class to toggle
8901          * @return {Roo.Element} this
8902          */
8903         toggleClass : function(className){
8904             if(this.hasClass(className)){
8905                 this.removeClass(className);
8906             }else{
8907                 this.addClass(className);
8908             }
8909             return this;
8910         },
8911
8912         /**
8913          * Checks if the specified CSS class exists on this element's DOM node.
8914          * @param {String} className The CSS class to check for
8915          * @return {Boolean} True if the class exists, else false
8916          */
8917         hasClass : function(className){
8918             if (this.dom instanceof SVGElement) {
8919                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8920             } 
8921             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8922         },
8923
8924         /**
8925          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8926          * @param {String} oldClassName The CSS class to replace
8927          * @param {String} newClassName The replacement CSS class
8928          * @return {Roo.Element} this
8929          */
8930         replaceClass : function(oldClassName, newClassName){
8931             this.removeClass(oldClassName);
8932             this.addClass(newClassName);
8933             return this;
8934         },
8935
8936         /**
8937          * Returns an object with properties matching the styles requested.
8938          * For example, el.getStyles('color', 'font-size', 'width') might return
8939          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8940          * @param {String} style1 A style name
8941          * @param {String} style2 A style name
8942          * @param {String} etc.
8943          * @return {Object} The style object
8944          */
8945         getStyles : function(){
8946             var a = arguments, len = a.length, r = {};
8947             for(var i = 0; i < len; i++){
8948                 r[a[i]] = this.getStyle(a[i]);
8949             }
8950             return r;
8951         },
8952
8953         /**
8954          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8955          * @param {String} property The style property whose value is returned.
8956          * @return {String} The current value of the style property for this element.
8957          */
8958         getStyle : function(){
8959             return view && view.getComputedStyle ?
8960                 function(prop){
8961                     var el = this.dom, v, cs, camel;
8962                     if(prop == 'float'){
8963                         prop = "cssFloat";
8964                     }
8965                     if(el.style && (v = el.style[prop])){
8966                         return v;
8967                     }
8968                     if(cs = view.getComputedStyle(el, "")){
8969                         if(!(camel = propCache[prop])){
8970                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8971                         }
8972                         return cs[camel];
8973                     }
8974                     return null;
8975                 } :
8976                 function(prop){
8977                     var el = this.dom, v, cs, camel;
8978                     if(prop == 'opacity'){
8979                         if(typeof el.style.filter == 'string'){
8980                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8981                             if(m){
8982                                 var fv = parseFloat(m[1]);
8983                                 if(!isNaN(fv)){
8984                                     return fv ? fv / 100 : 0;
8985                                 }
8986                             }
8987                         }
8988                         return 1;
8989                     }else if(prop == 'float'){
8990                         prop = "styleFloat";
8991                     }
8992                     if(!(camel = propCache[prop])){
8993                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8994                     }
8995                     if(v = el.style[camel]){
8996                         return v;
8997                     }
8998                     if(cs = el.currentStyle){
8999                         return cs[camel];
9000                     }
9001                     return null;
9002                 };
9003         }(),
9004
9005         /**
9006          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
9007          * @param {String/Object} property The style property to be set, or an object of multiple styles.
9008          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
9009          * @return {Roo.Element} this
9010          */
9011         setStyle : function(prop, value){
9012             if(typeof prop == "string"){
9013                 
9014                 if (prop == 'float') {
9015                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9016                     return this;
9017                 }
9018                 
9019                 var camel;
9020                 if(!(camel = propCache[prop])){
9021                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9022                 }
9023                 
9024                 if(camel == 'opacity') {
9025                     this.setOpacity(value);
9026                 }else{
9027                     this.dom.style[camel] = value;
9028                 }
9029             }else{
9030                 for(var style in prop){
9031                     if(typeof prop[style] != "function"){
9032                        this.setStyle(style, prop[style]);
9033                     }
9034                 }
9035             }
9036             return this;
9037         },
9038
9039         /**
9040          * More flexible version of {@link #setStyle} for setting style properties.
9041          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9042          * a function which returns such a specification.
9043          * @return {Roo.Element} this
9044          */
9045         applyStyles : function(style){
9046             Roo.DomHelper.applyStyles(this.dom, style);
9047             return this;
9048         },
9049
9050         /**
9051           * 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).
9052           * @return {Number} The X position of the element
9053           */
9054         getX : function(){
9055             return D.getX(this.dom);
9056         },
9057
9058         /**
9059           * 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).
9060           * @return {Number} The Y position of the element
9061           */
9062         getY : function(){
9063             return D.getY(this.dom);
9064         },
9065
9066         /**
9067           * 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).
9068           * @return {Array} The XY position of the element
9069           */
9070         getXY : function(){
9071             return D.getXY(this.dom);
9072         },
9073
9074         /**
9075          * 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).
9076          * @param {Number} The X position of the element
9077          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9078          * @return {Roo.Element} this
9079          */
9080         setX : function(x, animate){
9081             if(!animate || !A){
9082                 D.setX(this.dom, x);
9083             }else{
9084                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9085             }
9086             return this;
9087         },
9088
9089         /**
9090          * 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).
9091          * @param {Number} The Y position of the element
9092          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9093          * @return {Roo.Element} this
9094          */
9095         setY : function(y, animate){
9096             if(!animate || !A){
9097                 D.setY(this.dom, y);
9098             }else{
9099                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9100             }
9101             return this;
9102         },
9103
9104         /**
9105          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9106          * @param {String} left The left CSS property value
9107          * @return {Roo.Element} this
9108          */
9109         setLeft : function(left){
9110             this.setStyle("left", this.addUnits(left));
9111             return this;
9112         },
9113
9114         /**
9115          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9116          * @param {String} top The top CSS property value
9117          * @return {Roo.Element} this
9118          */
9119         setTop : function(top){
9120             this.setStyle("top", this.addUnits(top));
9121             return this;
9122         },
9123
9124         /**
9125          * Sets the element's CSS right style.
9126          * @param {String} right The right CSS property value
9127          * @return {Roo.Element} this
9128          */
9129         setRight : function(right){
9130             this.setStyle("right", this.addUnits(right));
9131             return this;
9132         },
9133
9134         /**
9135          * Sets the element's CSS bottom style.
9136          * @param {String} bottom The bottom CSS property value
9137          * @return {Roo.Element} this
9138          */
9139         setBottom : function(bottom){
9140             this.setStyle("bottom", this.addUnits(bottom));
9141             return this;
9142         },
9143
9144         /**
9145          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9146          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9147          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9148          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9149          * @return {Roo.Element} this
9150          */
9151         setXY : function(pos, animate){
9152             if(!animate || !A){
9153                 D.setXY(this.dom, pos);
9154             }else{
9155                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9156             }
9157             return this;
9158         },
9159
9160         /**
9161          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9162          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9163          * @param {Number} x X value for new position (coordinates are page-based)
9164          * @param {Number} y Y value for new position (coordinates are page-based)
9165          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9166          * @return {Roo.Element} this
9167          */
9168         setLocation : function(x, y, animate){
9169             this.setXY([x, y], this.preanim(arguments, 2));
9170             return this;
9171         },
9172
9173         /**
9174          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9175          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9176          * @param {Number} x X value for new position (coordinates are page-based)
9177          * @param {Number} y Y value for new position (coordinates are page-based)
9178          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9179          * @return {Roo.Element} this
9180          */
9181         moveTo : function(x, y, animate){
9182             this.setXY([x, y], this.preanim(arguments, 2));
9183             return this;
9184         },
9185
9186         /**
9187          * Returns the region of the given element.
9188          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9189          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9190          */
9191         getRegion : function(){
9192             return D.getRegion(this.dom);
9193         },
9194
9195         /**
9196          * Returns the offset height of the element
9197          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9198          * @return {Number} The element's height
9199          */
9200         getHeight : function(contentHeight){
9201             var h = this.dom.offsetHeight || 0;
9202             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9203         },
9204
9205         /**
9206          * Returns the offset width of the element
9207          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9208          * @return {Number} The element's width
9209          */
9210         getWidth : function(contentWidth){
9211             var w = this.dom.offsetWidth || 0;
9212             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9213         },
9214
9215         /**
9216          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9217          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9218          * if a height has not been set using CSS.
9219          * @return {Number}
9220          */
9221         getComputedHeight : function(){
9222             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9223             if(!h){
9224                 h = parseInt(this.getStyle('height'), 10) || 0;
9225                 if(!this.isBorderBox()){
9226                     h += this.getFrameWidth('tb');
9227                 }
9228             }
9229             return h;
9230         },
9231
9232         /**
9233          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9234          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9235          * if a width has not been set using CSS.
9236          * @return {Number}
9237          */
9238         getComputedWidth : function(){
9239             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9240             if(!w){
9241                 w = parseInt(this.getStyle('width'), 10) || 0;
9242                 if(!this.isBorderBox()){
9243                     w += this.getFrameWidth('lr');
9244                 }
9245             }
9246             return w;
9247         },
9248
9249         /**
9250          * Returns the size of the element.
9251          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9252          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9253          */
9254         getSize : function(contentSize){
9255             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9256         },
9257
9258         /**
9259          * Returns the width and height of the viewport.
9260          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9261          */
9262         getViewSize : function(){
9263             var d = this.dom, doc = document, aw = 0, ah = 0;
9264             if(d == doc || d == doc.body){
9265                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9266             }else{
9267                 return {
9268                     width : d.clientWidth,
9269                     height: d.clientHeight
9270                 };
9271             }
9272         },
9273
9274         /**
9275          * Returns the value of the "value" attribute
9276          * @param {Boolean} asNumber true to parse the value as a number
9277          * @return {String/Number}
9278          */
9279         getValue : function(asNumber){
9280             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9281         },
9282
9283         // private
9284         adjustWidth : function(width){
9285             if(typeof width == "number"){
9286                 if(this.autoBoxAdjust && !this.isBorderBox()){
9287                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9288                 }
9289                 if(width < 0){
9290                     width = 0;
9291                 }
9292             }
9293             return width;
9294         },
9295
9296         // private
9297         adjustHeight : function(height){
9298             if(typeof height == "number"){
9299                if(this.autoBoxAdjust && !this.isBorderBox()){
9300                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9301                }
9302                if(height < 0){
9303                    height = 0;
9304                }
9305             }
9306             return height;
9307         },
9308
9309         /**
9310          * Set the width of the element
9311          * @param {Number} width The new width
9312          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9313          * @return {Roo.Element} this
9314          */
9315         setWidth : function(width, animate){
9316             width = this.adjustWidth(width);
9317             if(!animate || !A){
9318                 this.dom.style.width = this.addUnits(width);
9319             }else{
9320                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9321             }
9322             return this;
9323         },
9324
9325         /**
9326          * Set the height of the element
9327          * @param {Number} height The new height
9328          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9329          * @return {Roo.Element} this
9330          */
9331          setHeight : function(height, animate){
9332             height = this.adjustHeight(height);
9333             if(!animate || !A){
9334                 this.dom.style.height = this.addUnits(height);
9335             }else{
9336                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9337             }
9338             return this;
9339         },
9340
9341         /**
9342          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9343          * @param {Number} width The new width
9344          * @param {Number} height The new height
9345          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9346          * @return {Roo.Element} this
9347          */
9348          setSize : function(width, height, animate){
9349             if(typeof width == "object"){ // in case of object from getSize()
9350                 height = width.height; width = width.width;
9351             }
9352             width = this.adjustWidth(width); height = this.adjustHeight(height);
9353             if(!animate || !A){
9354                 this.dom.style.width = this.addUnits(width);
9355                 this.dom.style.height = this.addUnits(height);
9356             }else{
9357                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9358             }
9359             return this;
9360         },
9361
9362         /**
9363          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9364          * @param {Number} x X value for new position (coordinates are page-based)
9365          * @param {Number} y Y value for new position (coordinates are page-based)
9366          * @param {Number} width The new width
9367          * @param {Number} height The new height
9368          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9369          * @return {Roo.Element} this
9370          */
9371         setBounds : function(x, y, width, height, animate){
9372             if(!animate || !A){
9373                 this.setSize(width, height);
9374                 this.setLocation(x, y);
9375             }else{
9376                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9377                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9378                               this.preanim(arguments, 4), 'motion');
9379             }
9380             return this;
9381         },
9382
9383         /**
9384          * 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.
9385          * @param {Roo.lib.Region} region The region to fill
9386          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9387          * @return {Roo.Element} this
9388          */
9389         setRegion : function(region, animate){
9390             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9391             return this;
9392         },
9393
9394         /**
9395          * Appends an event handler
9396          *
9397          * @param {String}   eventName     The type of event to append
9398          * @param {Function} fn        The method the event invokes
9399          * @param {Object} scope       (optional) The scope (this object) of the fn
9400          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9401          */
9402         addListener : function(eventName, fn, scope, options)
9403         {
9404             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9405                 this.addListener('touchstart', this.onTapHandler, this);
9406             }
9407             
9408             // we need to handle a special case where dom element is a svg element.
9409             // in this case we do not actua
9410             if (!this.dom) {
9411                 return;
9412             }
9413             
9414             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9415                 if (typeof(this.listeners[eventName]) == 'undefined') {
9416                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9417                 }
9418                 this.listeners[eventName].addListener(fn, scope, options);
9419                 return;
9420             }
9421             
9422                 
9423             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9424             
9425             
9426         },
9427         tapedTwice : false,
9428         onTapHandler : function(event)
9429         {
9430             if(!this.tapedTwice) {
9431                 this.tapedTwice = true;
9432                 var s = this;
9433                 setTimeout( function() {
9434                     s.tapedTwice = false;
9435                 }, 300 );
9436                 return;
9437             }
9438             event.preventDefault();
9439             var revent = new MouseEvent('dblclick',  {
9440                 view: window,
9441                 bubbles: true,
9442                 cancelable: true
9443             });
9444              
9445             this.dom.dispatchEvent(revent);
9446             //action on double tap goes below
9447              
9448         }, 
9449  
9450         /**
9451          * Removes an event handler from this element
9452          * @param {String} eventName the type of event to remove
9453          * @param {Function} fn the method the event invokes
9454          * @param {Function} scope (needed for svg fake listeners)
9455          * @return {Roo.Element} this
9456          */
9457         removeListener : function(eventName, fn, scope){
9458             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9459             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9460                 return this;
9461             }
9462             this.listeners[eventName].removeListener(fn, scope);
9463             return this;
9464         },
9465
9466         /**
9467          * Removes all previous added listeners from this element
9468          * @return {Roo.Element} this
9469          */
9470         removeAllListeners : function(){
9471             E.purgeElement(this.dom);
9472             this.listeners = {};
9473             return this;
9474         },
9475
9476         relayEvent : function(eventName, observable){
9477             this.on(eventName, function(e){
9478                 observable.fireEvent(eventName, e);
9479             });
9480         },
9481
9482         
9483         /**
9484          * Set the opacity of the element
9485          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9486          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9487          * @return {Roo.Element} this
9488          */
9489          setOpacity : function(opacity, animate){
9490             if(!animate || !A){
9491                 var s = this.dom.style;
9492                 if(Roo.isIE){
9493                     s.zoom = 1;
9494                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9495                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9496                 }else{
9497                     s.opacity = opacity;
9498                 }
9499             }else{
9500                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9501             }
9502             return this;
9503         },
9504
9505         /**
9506          * Gets the left X coordinate
9507          * @param {Boolean} local True to get the local css position instead of page coordinate
9508          * @return {Number}
9509          */
9510         getLeft : function(local){
9511             if(!local){
9512                 return this.getX();
9513             }else{
9514                 return parseInt(this.getStyle("left"), 10) || 0;
9515             }
9516         },
9517
9518         /**
9519          * Gets the right X coordinate of the element (element X position + element width)
9520          * @param {Boolean} local True to get the local css position instead of page coordinate
9521          * @return {Number}
9522          */
9523         getRight : function(local){
9524             if(!local){
9525                 return this.getX() + this.getWidth();
9526             }else{
9527                 return (this.getLeft(true) + this.getWidth()) || 0;
9528             }
9529         },
9530
9531         /**
9532          * Gets the top Y coordinate
9533          * @param {Boolean} local True to get the local css position instead of page coordinate
9534          * @return {Number}
9535          */
9536         getTop : function(local) {
9537             if(!local){
9538                 return this.getY();
9539             }else{
9540                 return parseInt(this.getStyle("top"), 10) || 0;
9541             }
9542         },
9543
9544         /**
9545          * Gets the bottom Y coordinate of the element (element Y position + element height)
9546          * @param {Boolean} local True to get the local css position instead of page coordinate
9547          * @return {Number}
9548          */
9549         getBottom : function(local){
9550             if(!local){
9551                 return this.getY() + this.getHeight();
9552             }else{
9553                 return (this.getTop(true) + this.getHeight()) || 0;
9554             }
9555         },
9556
9557         /**
9558         * Initializes positioning on this element. If a desired position is not passed, it will make the
9559         * the element positioned relative IF it is not already positioned.
9560         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9561         * @param {Number} zIndex (optional) The zIndex to apply
9562         * @param {Number} x (optional) Set the page X position
9563         * @param {Number} y (optional) Set the page Y position
9564         */
9565         position : function(pos, zIndex, x, y){
9566             if(!pos){
9567                if(this.getStyle('position') == 'static'){
9568                    this.setStyle('position', 'relative');
9569                }
9570             }else{
9571                 this.setStyle("position", pos);
9572             }
9573             if(zIndex){
9574                 this.setStyle("z-index", zIndex);
9575             }
9576             if(x !== undefined && y !== undefined){
9577                 this.setXY([x, y]);
9578             }else if(x !== undefined){
9579                 this.setX(x);
9580             }else if(y !== undefined){
9581                 this.setY(y);
9582             }
9583         },
9584
9585         /**
9586         * Clear positioning back to the default when the document was loaded
9587         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9588         * @return {Roo.Element} this
9589          */
9590         clearPositioning : function(value){
9591             value = value ||'';
9592             this.setStyle({
9593                 "left": value,
9594                 "right": value,
9595                 "top": value,
9596                 "bottom": value,
9597                 "z-index": "",
9598                 "position" : "static"
9599             });
9600             return this;
9601         },
9602
9603         /**
9604         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9605         * snapshot before performing an update and then restoring the element.
9606         * @return {Object}
9607         */
9608         getPositioning : function(){
9609             var l = this.getStyle("left");
9610             var t = this.getStyle("top");
9611             return {
9612                 "position" : this.getStyle("position"),
9613                 "left" : l,
9614                 "right" : l ? "" : this.getStyle("right"),
9615                 "top" : t,
9616                 "bottom" : t ? "" : this.getStyle("bottom"),
9617                 "z-index" : this.getStyle("z-index")
9618             };
9619         },
9620
9621         /**
9622          * Gets the width of the border(s) for the specified side(s)
9623          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9624          * passing lr would get the border (l)eft width + the border (r)ight width.
9625          * @return {Number} The width of the sides passed added together
9626          */
9627         getBorderWidth : function(side){
9628             return this.addStyles(side, El.borders);
9629         },
9630
9631         /**
9632          * Gets the width of the padding(s) for the specified side(s)
9633          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9634          * passing lr would get the padding (l)eft + the padding (r)ight.
9635          * @return {Number} The padding of the sides passed added together
9636          */
9637         getPadding : function(side){
9638             return this.addStyles(side, El.paddings);
9639         },
9640
9641         /**
9642         * Set positioning with an object returned by getPositioning().
9643         * @param {Object} posCfg
9644         * @return {Roo.Element} this
9645          */
9646         setPositioning : function(pc){
9647             this.applyStyles(pc);
9648             if(pc.right == "auto"){
9649                 this.dom.style.right = "";
9650             }
9651             if(pc.bottom == "auto"){
9652                 this.dom.style.bottom = "";
9653             }
9654             return this;
9655         },
9656
9657         // private
9658         fixDisplay : function(){
9659             if(this.getStyle("display") == "none"){
9660                 this.setStyle("visibility", "hidden");
9661                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9662                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9663                     this.setStyle("display", "block");
9664                 }
9665             }
9666         },
9667
9668         /**
9669          * Quick set left and top adding default units
9670          * @param {String} left The left CSS property value
9671          * @param {String} top The top CSS property value
9672          * @return {Roo.Element} this
9673          */
9674          setLeftTop : function(left, top){
9675             this.dom.style.left = this.addUnits(left);
9676             this.dom.style.top = this.addUnits(top);
9677             return this;
9678         },
9679
9680         /**
9681          * Move this element relative to its current position.
9682          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9683          * @param {Number} distance How far to move the element in pixels
9684          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9685          * @return {Roo.Element} this
9686          */
9687          move : function(direction, distance, animate){
9688             var xy = this.getXY();
9689             direction = direction.toLowerCase();
9690             switch(direction){
9691                 case "l":
9692                 case "left":
9693                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9694                     break;
9695                case "r":
9696                case "right":
9697                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9698                     break;
9699                case "t":
9700                case "top":
9701                case "up":
9702                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9703                     break;
9704                case "b":
9705                case "bottom":
9706                case "down":
9707                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9708                     break;
9709             }
9710             return this;
9711         },
9712
9713         /**
9714          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9715          * @return {Roo.Element} this
9716          */
9717         clip : function(){
9718             if(!this.isClipped){
9719                this.isClipped = true;
9720                this.originalClip = {
9721                    "o": this.getStyle("overflow"),
9722                    "x": this.getStyle("overflow-x"),
9723                    "y": this.getStyle("overflow-y")
9724                };
9725                this.setStyle("overflow", "hidden");
9726                this.setStyle("overflow-x", "hidden");
9727                this.setStyle("overflow-y", "hidden");
9728             }
9729             return this;
9730         },
9731
9732         /**
9733          *  Return clipping (overflow) to original clipping before clip() was called
9734          * @return {Roo.Element} this
9735          */
9736         unclip : function(){
9737             if(this.isClipped){
9738                 this.isClipped = false;
9739                 var o = this.originalClip;
9740                 if(o.o){this.setStyle("overflow", o.o);}
9741                 if(o.x){this.setStyle("overflow-x", o.x);}
9742                 if(o.y){this.setStyle("overflow-y", o.y);}
9743             }
9744             return this;
9745         },
9746
9747
9748         /**
9749          * Gets the x,y coordinates specified by the anchor position on the element.
9750          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9751          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9752          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9753          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9754          * @return {Array} [x, y] An array containing the element's x and y coordinates
9755          */
9756         getAnchorXY : function(anchor, local, s){
9757             //Passing a different size is useful for pre-calculating anchors,
9758             //especially for anchored animations that change the el size.
9759
9760             var w, h, vp = false;
9761             if(!s){
9762                 var d = this.dom;
9763                 if(d == document.body || d == document){
9764                     vp = true;
9765                     w = D.getViewWidth(); h = D.getViewHeight();
9766                 }else{
9767                     w = this.getWidth(); h = this.getHeight();
9768                 }
9769             }else{
9770                 w = s.width;  h = s.height;
9771             }
9772             var x = 0, y = 0, r = Math.round;
9773             switch((anchor || "tl").toLowerCase()){
9774                 case "c":
9775                     x = r(w*.5);
9776                     y = r(h*.5);
9777                 break;
9778                 case "t":
9779                     x = r(w*.5);
9780                     y = 0;
9781                 break;
9782                 case "l":
9783                     x = 0;
9784                     y = r(h*.5);
9785                 break;
9786                 case "r":
9787                     x = w;
9788                     y = r(h*.5);
9789                 break;
9790                 case "b":
9791                     x = r(w*.5);
9792                     y = h;
9793                 break;
9794                 case "tl":
9795                     x = 0;
9796                     y = 0;
9797                 break;
9798                 case "bl":
9799                     x = 0;
9800                     y = h;
9801                 break;
9802                 case "br":
9803                     x = w;
9804                     y = h;
9805                 break;
9806                 case "tr":
9807                     x = w;
9808                     y = 0;
9809                 break;
9810             }
9811             if(local === true){
9812                 return [x, y];
9813             }
9814             if(vp){
9815                 var sc = this.getScroll();
9816                 return [x + sc.left, y + sc.top];
9817             }
9818             //Add the element's offset xy
9819             var o = this.getXY();
9820             return [x+o[0], y+o[1]];
9821         },
9822
9823         /**
9824          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9825          * supported position values.
9826          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9827          * @param {String} position The position to align to.
9828          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9829          * @return {Array} [x, y]
9830          */
9831         getAlignToXY : function(el, p, o)
9832         {
9833             el = Roo.get(el);
9834             var d = this.dom;
9835             if(!el.dom){
9836                 throw "Element.alignTo with an element that doesn't exist";
9837             }
9838             var c = false; //constrain to viewport
9839             var p1 = "", p2 = "";
9840             o = o || [0,0];
9841
9842             if(!p){
9843                 p = "tl-bl";
9844             }else if(p == "?"){
9845                 p = "tl-bl?";
9846             }else if(p.indexOf("-") == -1){
9847                 p = "tl-" + p;
9848             }
9849             p = p.toLowerCase();
9850             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9851             if(!m){
9852                throw "Element.alignTo with an invalid alignment " + p;
9853             }
9854             p1 = m[1]; p2 = m[2]; c = !!m[3];
9855
9856             //Subtract the aligned el's internal xy from the target's offset xy
9857             //plus custom offset to get the aligned el's new offset xy
9858             var a1 = this.getAnchorXY(p1, true);
9859             var a2 = el.getAnchorXY(p2, false);
9860             var x = a2[0] - a1[0] + o[0];
9861             var y = a2[1] - a1[1] + o[1];
9862             if(c){
9863                 //constrain the aligned el to viewport if necessary
9864                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9865                 // 5px of margin for ie
9866                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9867
9868                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9869                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9870                 //otherwise swap the aligned el to the opposite border of the target.
9871                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9872                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9873                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9874                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9875
9876                var doc = document;
9877                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9878                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9879
9880                if((x+w) > dw + scrollX){
9881                     x = swapX ? r.left-w : dw+scrollX-w;
9882                 }
9883                if(x < scrollX){
9884                    x = swapX ? r.right : scrollX;
9885                }
9886                if((y+h) > dh + scrollY){
9887                     y = swapY ? r.top-h : dh+scrollY-h;
9888                 }
9889                if (y < scrollY){
9890                    y = swapY ? r.bottom : scrollY;
9891                }
9892             }
9893             return [x,y];
9894         },
9895
9896         // private
9897         getConstrainToXY : function(){
9898             var os = {top:0, left:0, bottom:0, right: 0};
9899
9900             return function(el, local, offsets, proposedXY){
9901                 el = Roo.get(el);
9902                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9903
9904                 var vw, vh, vx = 0, vy = 0;
9905                 if(el.dom == document.body || el.dom == document){
9906                     vw = Roo.lib.Dom.getViewWidth();
9907                     vh = Roo.lib.Dom.getViewHeight();
9908                 }else{
9909                     vw = el.dom.clientWidth;
9910                     vh = el.dom.clientHeight;
9911                     if(!local){
9912                         var vxy = el.getXY();
9913                         vx = vxy[0];
9914                         vy = vxy[1];
9915                     }
9916                 }
9917
9918                 var s = el.getScroll();
9919
9920                 vx += offsets.left + s.left;
9921                 vy += offsets.top + s.top;
9922
9923                 vw -= offsets.right;
9924                 vh -= offsets.bottom;
9925
9926                 var vr = vx+vw;
9927                 var vb = vy+vh;
9928
9929                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9930                 var x = xy[0], y = xy[1];
9931                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9932
9933                 // only move it if it needs it
9934                 var moved = false;
9935
9936                 // first validate right/bottom
9937                 if((x + w) > vr){
9938                     x = vr - w;
9939                     moved = true;
9940                 }
9941                 if((y + h) > vb){
9942                     y = vb - h;
9943                     moved = true;
9944                 }
9945                 // then make sure top/left isn't negative
9946                 if(x < vx){
9947                     x = vx;
9948                     moved = true;
9949                 }
9950                 if(y < vy){
9951                     y = vy;
9952                     moved = true;
9953                 }
9954                 return moved ? [x, y] : false;
9955             };
9956         }(),
9957
9958         // private
9959         adjustForConstraints : function(xy, parent, offsets){
9960             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9961         },
9962
9963         /**
9964          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9965          * document it aligns it to the viewport.
9966          * The position parameter is optional, and can be specified in any one of the following formats:
9967          * <ul>
9968          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9969          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9970          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9971          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9972          *   <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
9973          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9974          * </ul>
9975          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9976          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9977          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9978          * that specified in order to enforce the viewport constraints.
9979          * Following are all of the supported anchor positions:
9980     <pre>
9981     Value  Description
9982     -----  -----------------------------
9983     tl     The top left corner (default)
9984     t      The center of the top edge
9985     tr     The top right corner
9986     l      The center of the left edge
9987     c      In the center of the element
9988     r      The center of the right edge
9989     bl     The bottom left corner
9990     b      The center of the bottom edge
9991     br     The bottom right corner
9992     </pre>
9993     Example Usage:
9994     <pre><code>
9995     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9996     el.alignTo("other-el");
9997
9998     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9999     el.alignTo("other-el", "tr?");
10000
10001     // align the bottom right corner of el with the center left edge of other-el
10002     el.alignTo("other-el", "br-l?");
10003
10004     // align the center of el with the bottom left corner of other-el and
10005     // adjust the x position by -6 pixels (and the y position by 0)
10006     el.alignTo("other-el", "c-bl", [-6, 0]);
10007     </code></pre>
10008          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10009          * @param {String} position The position to align to.
10010          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10011          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10012          * @return {Roo.Element} this
10013          */
10014         alignTo : function(element, position, offsets, animate){
10015             var xy = this.getAlignToXY(element, position, offsets);
10016             this.setXY(xy, this.preanim(arguments, 3));
10017             return this;
10018         },
10019
10020         /**
10021          * Anchors an element to another element and realigns it when the window is resized.
10022          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10023          * @param {String} position The position to align to.
10024          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10025          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10026          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10027          * is a number, it is used as the buffer delay (defaults to 50ms).
10028          * @param {Function} callback The function to call after the animation finishes
10029          * @return {Roo.Element} this
10030          */
10031         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10032             var action = function(){
10033                 this.alignTo(el, alignment, offsets, animate);
10034                 Roo.callback(callback, this);
10035             };
10036             Roo.EventManager.onWindowResize(action, this);
10037             var tm = typeof monitorScroll;
10038             if(tm != 'undefined'){
10039                 Roo.EventManager.on(window, 'scroll', action, this,
10040                     {buffer: tm == 'number' ? monitorScroll : 50});
10041             }
10042             action.call(this); // align immediately
10043             return this;
10044         },
10045         /**
10046          * Clears any opacity settings from this element. Required in some cases for IE.
10047          * @return {Roo.Element} this
10048          */
10049         clearOpacity : function(){
10050             if (window.ActiveXObject) {
10051                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10052                     this.dom.style.filter = "";
10053                 }
10054             } else {
10055                 this.dom.style.opacity = "";
10056                 this.dom.style["-moz-opacity"] = "";
10057                 this.dom.style["-khtml-opacity"] = "";
10058             }
10059             return this;
10060         },
10061
10062         /**
10063          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10064          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10065          * @return {Roo.Element} this
10066          */
10067         hide : function(animate){
10068             this.setVisible(false, this.preanim(arguments, 0));
10069             return this;
10070         },
10071
10072         /**
10073         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10074         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10075          * @return {Roo.Element} this
10076          */
10077         show : function(animate){
10078             this.setVisible(true, this.preanim(arguments, 0));
10079             return this;
10080         },
10081
10082         /**
10083          * @private Test if size has a unit, otherwise appends the default
10084          */
10085         addUnits : function(size){
10086             return Roo.Element.addUnits(size, this.defaultUnit);
10087         },
10088
10089         /**
10090          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10091          * @return {Roo.Element} this
10092          */
10093         beginMeasure : function(){
10094             var el = this.dom;
10095             if(el.offsetWidth || el.offsetHeight){
10096                 return this; // offsets work already
10097             }
10098             var changed = [];
10099             var p = this.dom, b = document.body; // start with this element
10100             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10101                 var pe = Roo.get(p);
10102                 if(pe.getStyle('display') == 'none'){
10103                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10104                     p.style.visibility = "hidden";
10105                     p.style.display = "block";
10106                 }
10107                 p = p.parentNode;
10108             }
10109             this._measureChanged = changed;
10110             return this;
10111
10112         },
10113
10114         /**
10115          * Restores displays to before beginMeasure was called
10116          * @return {Roo.Element} this
10117          */
10118         endMeasure : function(){
10119             var changed = this._measureChanged;
10120             if(changed){
10121                 for(var i = 0, len = changed.length; i < len; i++) {
10122                     var r = changed[i];
10123                     r.el.style.visibility = r.visibility;
10124                     r.el.style.display = "none";
10125                 }
10126                 this._measureChanged = null;
10127             }
10128             return this;
10129         },
10130
10131         /**
10132         * Update the innerHTML of this element, optionally searching for and processing scripts
10133         * @param {String} html The new HTML
10134         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10135         * @param {Function} callback For async script loading you can be noticed when the update completes
10136         * @return {Roo.Element} this
10137          */
10138         update : function(html, loadScripts, callback){
10139             if(typeof html == "undefined"){
10140                 html = "";
10141             }
10142             if(loadScripts !== true){
10143                 this.dom.innerHTML = html;
10144                 if(typeof callback == "function"){
10145                     callback();
10146                 }
10147                 return this;
10148             }
10149             var id = Roo.id();
10150             var dom = this.dom;
10151
10152             html += '<span id="' + id + '"></span>';
10153
10154             E.onAvailable(id, function(){
10155                 var hd = document.getElementsByTagName("head")[0];
10156                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10157                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10158                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10159
10160                 var match;
10161                 while(match = re.exec(html)){
10162                     var attrs = match[1];
10163                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10164                     if(srcMatch && srcMatch[2]){
10165                        var s = document.createElement("script");
10166                        s.src = srcMatch[2];
10167                        var typeMatch = attrs.match(typeRe);
10168                        if(typeMatch && typeMatch[2]){
10169                            s.type = typeMatch[2];
10170                        }
10171                        hd.appendChild(s);
10172                     }else if(match[2] && match[2].length > 0){
10173                         if(window.execScript) {
10174                            window.execScript(match[2]);
10175                         } else {
10176                             /**
10177                              * eval:var:id
10178                              * eval:var:dom
10179                              * eval:var:html
10180                              * 
10181                              */
10182                            window.eval(match[2]);
10183                         }
10184                     }
10185                 }
10186                 var el = document.getElementById(id);
10187                 if(el){el.parentNode.removeChild(el);}
10188                 if(typeof callback == "function"){
10189                     callback();
10190                 }
10191             });
10192             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10193             return this;
10194         },
10195
10196         /**
10197          * Direct access to the UpdateManager update() method (takes the same parameters).
10198          * @param {String/Function} url The url for this request or a function to call to get the url
10199          * @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}
10200          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10201          * @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.
10202          * @return {Roo.Element} this
10203          */
10204         load : function(){
10205             var um = this.getUpdateManager();
10206             um.update.apply(um, arguments);
10207             return this;
10208         },
10209
10210         /**
10211         * Gets this element's UpdateManager
10212         * @return {Roo.UpdateManager} The UpdateManager
10213         */
10214         getUpdateManager : function(){
10215             if(!this.updateManager){
10216                 this.updateManager = new Roo.UpdateManager(this);
10217             }
10218             return this.updateManager;
10219         },
10220
10221         /**
10222          * Disables text selection for this element (normalized across browsers)
10223          * @return {Roo.Element} this
10224          */
10225         unselectable : function(){
10226             this.dom.unselectable = "on";
10227             this.swallowEvent("selectstart", true);
10228             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10229             this.addClass("x-unselectable");
10230             return this;
10231         },
10232
10233         /**
10234         * Calculates the x, y to center this element on the screen
10235         * @return {Array} The x, y values [x, y]
10236         */
10237         getCenterXY : function(){
10238             return this.getAlignToXY(document, 'c-c');
10239         },
10240
10241         /**
10242         * Centers the Element in either the viewport, or another Element.
10243         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10244         */
10245         center : function(centerIn){
10246             this.alignTo(centerIn || document, 'c-c');
10247             return this;
10248         },
10249
10250         /**
10251          * Tests various css rules/browsers to determine if this element uses a border box
10252          * @return {Boolean}
10253          */
10254         isBorderBox : function(){
10255             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10256         },
10257
10258         /**
10259          * Return a box {x, y, width, height} that can be used to set another elements
10260          * size/location to match this element.
10261          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10262          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10263          * @return {Object} box An object in the format {x, y, width, height}
10264          */
10265         getBox : function(contentBox, local){
10266             var xy;
10267             if(!local){
10268                 xy = this.getXY();
10269             }else{
10270                 var left = parseInt(this.getStyle("left"), 10) || 0;
10271                 var top = parseInt(this.getStyle("top"), 10) || 0;
10272                 xy = [left, top];
10273             }
10274             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10275             if(!contentBox){
10276                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10277             }else{
10278                 var l = this.getBorderWidth("l")+this.getPadding("l");
10279                 var r = this.getBorderWidth("r")+this.getPadding("r");
10280                 var t = this.getBorderWidth("t")+this.getPadding("t");
10281                 var b = this.getBorderWidth("b")+this.getPadding("b");
10282                 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)};
10283             }
10284             bx.right = bx.x + bx.width;
10285             bx.bottom = bx.y + bx.height;
10286             return bx;
10287         },
10288
10289         /**
10290          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10291          for more information about the sides.
10292          * @param {String} sides
10293          * @return {Number}
10294          */
10295         getFrameWidth : function(sides, onlyContentBox){
10296             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10297         },
10298
10299         /**
10300          * 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.
10301          * @param {Object} box The box to fill {x, y, width, height}
10302          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10303          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10304          * @return {Roo.Element} this
10305          */
10306         setBox : function(box, adjust, animate){
10307             var w = box.width, h = box.height;
10308             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10309                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10310                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10311             }
10312             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10313             return this;
10314         },
10315
10316         /**
10317          * Forces the browser to repaint this element
10318          * @return {Roo.Element} this
10319          */
10320          repaint : function(){
10321             var dom = this.dom;
10322             this.addClass("x-repaint");
10323             setTimeout(function(){
10324                 Roo.get(dom).removeClass("x-repaint");
10325             }, 1);
10326             return this;
10327         },
10328
10329         /**
10330          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10331          * then it returns the calculated width of the sides (see getPadding)
10332          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10333          * @return {Object/Number}
10334          */
10335         getMargins : function(side){
10336             if(!side){
10337                 return {
10338                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10339                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10340                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10341                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10342                 };
10343             }else{
10344                 return this.addStyles(side, El.margins);
10345              }
10346         },
10347
10348         // private
10349         addStyles : function(sides, styles){
10350             var val = 0, v, w;
10351             for(var i = 0, len = sides.length; i < len; i++){
10352                 v = this.getStyle(styles[sides.charAt(i)]);
10353                 if(v){
10354                      w = parseInt(v, 10);
10355                      if(w){ val += w; }
10356                 }
10357             }
10358             return val;
10359         },
10360
10361         /**
10362          * Creates a proxy element of this element
10363          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10364          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10365          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10366          * @return {Roo.Element} The new proxy element
10367          */
10368         createProxy : function(config, renderTo, matchBox){
10369             if(renderTo){
10370                 renderTo = Roo.getDom(renderTo);
10371             }else{
10372                 renderTo = document.body;
10373             }
10374             config = typeof config == "object" ?
10375                 config : {tag : "div", cls: config};
10376             var proxy = Roo.DomHelper.append(renderTo, config, true);
10377             if(matchBox){
10378                proxy.setBox(this.getBox());
10379             }
10380             return proxy;
10381         },
10382
10383         /**
10384          * Puts a mask over this element to disable user interaction. Requires core.css.
10385          * This method can only be applied to elements which accept child nodes.
10386          * @param {String} msg (optional) A message to display in the mask
10387          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10388          * @return {Element} The mask  element
10389          */
10390         mask : function(msg, msgCls)
10391         {
10392             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10393                 this.setStyle("position", "relative");
10394             }
10395             if(!this._mask){
10396                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10397             }
10398             
10399             this.addClass("x-masked");
10400             this._mask.setDisplayed(true);
10401             
10402             // we wander
10403             var z = 0;
10404             var dom = this.dom;
10405             while (dom && dom.style) {
10406                 if (!isNaN(parseInt(dom.style.zIndex))) {
10407                     z = Math.max(z, parseInt(dom.style.zIndex));
10408                 }
10409                 dom = dom.parentNode;
10410             }
10411             // if we are masking the body - then it hides everything..
10412             if (this.dom == document.body) {
10413                 z = 1000000;
10414                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10415                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10416             }
10417            
10418             if(typeof msg == 'string'){
10419                 if(!this._maskMsg){
10420                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10421                         cls: "roo-el-mask-msg", 
10422                         cn: [
10423                             {
10424                                 tag: 'i',
10425                                 cls: 'fa fa-spinner fa-spin'
10426                             },
10427                             {
10428                                 tag: 'div'
10429                             }   
10430                         ]
10431                     }, true);
10432                 }
10433                 var mm = this._maskMsg;
10434                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10435                 if (mm.dom.lastChild) { // weird IE issue?
10436                     mm.dom.lastChild.innerHTML = msg;
10437                 }
10438                 mm.setDisplayed(true);
10439                 mm.center(this);
10440                 mm.setStyle('z-index', z + 102);
10441             }
10442             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10443                 this._mask.setHeight(this.getHeight());
10444             }
10445             this._mask.setStyle('z-index', z + 100);
10446             
10447             return this._mask;
10448         },
10449
10450         /**
10451          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10452          * it is cached for reuse.
10453          */
10454         unmask : function(removeEl){
10455             if(this._mask){
10456                 if(removeEl === true){
10457                     this._mask.remove();
10458                     delete this._mask;
10459                     if(this._maskMsg){
10460                         this._maskMsg.remove();
10461                         delete this._maskMsg;
10462                     }
10463                 }else{
10464                     this._mask.setDisplayed(false);
10465                     if(this._maskMsg){
10466                         this._maskMsg.setDisplayed(false);
10467                     }
10468                 }
10469             }
10470             this.removeClass("x-masked");
10471         },
10472
10473         /**
10474          * Returns true if this element is masked
10475          * @return {Boolean}
10476          */
10477         isMasked : function(){
10478             return this._mask && this._mask.isVisible();
10479         },
10480
10481         /**
10482          * Creates an iframe shim for this element to keep selects and other windowed objects from
10483          * showing through.
10484          * @return {Roo.Element} The new shim element
10485          */
10486         createShim : function(){
10487             var el = document.createElement('iframe');
10488             el.frameBorder = 'no';
10489             el.className = 'roo-shim';
10490             if(Roo.isIE && Roo.isSecure){
10491                 el.src = Roo.SSL_SECURE_URL;
10492             }
10493             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10494             shim.autoBoxAdjust = false;
10495             return shim;
10496         },
10497
10498         /**
10499          * Removes this element from the DOM and deletes it from the cache
10500          */
10501         remove : function(){
10502             if(this.dom.parentNode){
10503                 this.dom.parentNode.removeChild(this.dom);
10504             }
10505             delete El.cache[this.dom.id];
10506         },
10507
10508         /**
10509          * Sets up event handlers to add and remove a css class when the mouse is over this element
10510          * @param {String} className
10511          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10512          * mouseout events for children elements
10513          * @return {Roo.Element} this
10514          */
10515         addClassOnOver : function(className, preventFlicker){
10516             this.on("mouseover", function(){
10517                 Roo.fly(this, '_internal').addClass(className);
10518             }, this.dom);
10519             var removeFn = function(e){
10520                 if(preventFlicker !== true || !e.within(this, true)){
10521                     Roo.fly(this, '_internal').removeClass(className);
10522                 }
10523             };
10524             this.on("mouseout", removeFn, this.dom);
10525             return this;
10526         },
10527
10528         /**
10529          * Sets up event handlers to add and remove a css class when this element has the focus
10530          * @param {String} className
10531          * @return {Roo.Element} this
10532          */
10533         addClassOnFocus : function(className){
10534             this.on("focus", function(){
10535                 Roo.fly(this, '_internal').addClass(className);
10536             }, this.dom);
10537             this.on("blur", function(){
10538                 Roo.fly(this, '_internal').removeClass(className);
10539             }, this.dom);
10540             return this;
10541         },
10542         /**
10543          * 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)
10544          * @param {String} className
10545          * @return {Roo.Element} this
10546          */
10547         addClassOnClick : function(className){
10548             var dom = this.dom;
10549             this.on("mousedown", function(){
10550                 Roo.fly(dom, '_internal').addClass(className);
10551                 var d = Roo.get(document);
10552                 var fn = function(){
10553                     Roo.fly(dom, '_internal').removeClass(className);
10554                     d.removeListener("mouseup", fn);
10555                 };
10556                 d.on("mouseup", fn);
10557             });
10558             return this;
10559         },
10560
10561         /**
10562          * Stops the specified event from bubbling and optionally prevents the default action
10563          * @param {String} eventName
10564          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10565          * @return {Roo.Element} this
10566          */
10567         swallowEvent : function(eventName, preventDefault){
10568             var fn = function(e){
10569                 e.stopPropagation();
10570                 if(preventDefault){
10571                     e.preventDefault();
10572                 }
10573             };
10574             if(eventName instanceof Array){
10575                 for(var i = 0, len = eventName.length; i < len; i++){
10576                      this.on(eventName[i], fn);
10577                 }
10578                 return this;
10579             }
10580             this.on(eventName, fn);
10581             return this;
10582         },
10583
10584         /**
10585          * @private
10586          */
10587         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10588
10589         /**
10590          * Sizes this element to its parent element's dimensions performing
10591          * neccessary box adjustments.
10592          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10593          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10594          * @return {Roo.Element} this
10595          */
10596         fitToParent : function(monitorResize, targetParent) {
10597           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10598           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10599           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10600             return this;
10601           }
10602           var p = Roo.get(targetParent || this.dom.parentNode);
10603           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10604           if (monitorResize === true) {
10605             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10606             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10607           }
10608           return this;
10609         },
10610
10611         /**
10612          * Gets the next sibling, skipping text nodes
10613          * @return {HTMLElement} The next sibling or null
10614          */
10615         getNextSibling : function(){
10616             var n = this.dom.nextSibling;
10617             while(n && n.nodeType != 1){
10618                 n = n.nextSibling;
10619             }
10620             return n;
10621         },
10622
10623         /**
10624          * Gets the previous sibling, skipping text nodes
10625          * @return {HTMLElement} The previous sibling or null
10626          */
10627         getPrevSibling : function(){
10628             var n = this.dom.previousSibling;
10629             while(n && n.nodeType != 1){
10630                 n = n.previousSibling;
10631             }
10632             return n;
10633         },
10634
10635
10636         /**
10637          * Appends the passed element(s) to this element
10638          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10639          * @return {Roo.Element} this
10640          */
10641         appendChild: function(el){
10642             el = Roo.get(el);
10643             el.appendTo(this);
10644             return this;
10645         },
10646
10647         /**
10648          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10649          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10650          * automatically generated with the specified attributes.
10651          * @param {HTMLElement} insertBefore (optional) a child element of this element
10652          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10653          * @return {Roo.Element} The new child element
10654          */
10655         createChild: function(config, insertBefore, returnDom){
10656             config = config || {tag:'div'};
10657             if(insertBefore){
10658                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10659             }
10660             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10661         },
10662
10663         /**
10664          * Appends this element to the passed element
10665          * @param {String/HTMLElement/Element} el The new parent element
10666          * @return {Roo.Element} this
10667          */
10668         appendTo: function(el){
10669             el = Roo.getDom(el);
10670             el.appendChild(this.dom);
10671             return this;
10672         },
10673
10674         /**
10675          * Inserts this element before the passed element in the DOM
10676          * @param {String/HTMLElement/Element} el The element to insert before
10677          * @return {Roo.Element} this
10678          */
10679         insertBefore: function(el){
10680             el = Roo.getDom(el);
10681             el.parentNode.insertBefore(this.dom, el);
10682             return this;
10683         },
10684
10685         /**
10686          * Inserts this element after the passed element in the DOM
10687          * @param {String/HTMLElement/Element} el The element to insert after
10688          * @return {Roo.Element} this
10689          */
10690         insertAfter: function(el){
10691             el = Roo.getDom(el);
10692             el.parentNode.insertBefore(this.dom, el.nextSibling);
10693             return this;
10694         },
10695
10696         /**
10697          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10698          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10699          * @return {Roo.Element} The new child
10700          */
10701         insertFirst: function(el, returnDom){
10702             el = el || {};
10703             if(typeof el == 'object' && !el.nodeType){ // dh config
10704                 return this.createChild(el, this.dom.firstChild, returnDom);
10705             }else{
10706                 el = Roo.getDom(el);
10707                 this.dom.insertBefore(el, this.dom.firstChild);
10708                 return !returnDom ? Roo.get(el) : el;
10709             }
10710         },
10711
10712         /**
10713          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10714          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10715          * @param {String} where (optional) 'before' or 'after' defaults to before
10716          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10717          * @return {Roo.Element} the inserted Element
10718          */
10719         insertSibling: function(el, where, returnDom){
10720             where = where ? where.toLowerCase() : 'before';
10721             el = el || {};
10722             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10723
10724             if(typeof el == 'object' && !el.nodeType){ // dh config
10725                 if(where == 'after' && !this.dom.nextSibling){
10726                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10727                 }else{
10728                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10729                 }
10730
10731             }else{
10732                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10733                             where == 'before' ? this.dom : this.dom.nextSibling);
10734                 if(!returnDom){
10735                     rt = Roo.get(rt);
10736                 }
10737             }
10738             return rt;
10739         },
10740
10741         /**
10742          * Creates and wraps this element with another element
10743          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10744          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10745          * @return {HTMLElement/Element} The newly created wrapper element
10746          */
10747         wrap: function(config, returnDom){
10748             if(!config){
10749                 config = {tag: "div"};
10750             }
10751             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10752             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10753             return newEl;
10754         },
10755
10756         /**
10757          * Replaces the passed element with this element
10758          * @param {String/HTMLElement/Element} el The element to replace
10759          * @return {Roo.Element} this
10760          */
10761         replace: function(el){
10762             el = Roo.get(el);
10763             this.insertBefore(el);
10764             el.remove();
10765             return this;
10766         },
10767
10768         /**
10769          * Inserts an html fragment into this element
10770          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10771          * @param {String} html The HTML fragment
10772          * @param {Boolean} returnEl True to return an Roo.Element
10773          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10774          */
10775         insertHtml : function(where, html, returnEl){
10776             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10777             return returnEl ? Roo.get(el) : el;
10778         },
10779
10780         /**
10781          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10782          * @param {Object} o The object with the attributes
10783          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10784          * @return {Roo.Element} this
10785          */
10786         set : function(o, useSet){
10787             var el = this.dom;
10788             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10789             for(var attr in o){
10790                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10791                 if(attr=="cls"){
10792                     el.className = o["cls"];
10793                 }else{
10794                     if(useSet) {
10795                         el.setAttribute(attr, o[attr]);
10796                     } else {
10797                         el[attr] = o[attr];
10798                     }
10799                 }
10800             }
10801             if(o.style){
10802                 Roo.DomHelper.applyStyles(el, o.style);
10803             }
10804             return this;
10805         },
10806
10807         /**
10808          * Convenience method for constructing a KeyMap
10809          * @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:
10810          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10811          * @param {Function} fn The function to call
10812          * @param {Object} scope (optional) The scope of the function
10813          * @return {Roo.KeyMap} The KeyMap created
10814          */
10815         addKeyListener : function(key, fn, scope){
10816             var config;
10817             if(typeof key != "object" || key instanceof Array){
10818                 config = {
10819                     key: key,
10820                     fn: fn,
10821                     scope: scope
10822                 };
10823             }else{
10824                 config = {
10825                     key : key.key,
10826                     shift : key.shift,
10827                     ctrl : key.ctrl,
10828                     alt : key.alt,
10829                     fn: fn,
10830                     scope: scope
10831                 };
10832             }
10833             return new Roo.KeyMap(this, config);
10834         },
10835
10836         /**
10837          * Creates a KeyMap for this element
10838          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10839          * @return {Roo.KeyMap} The KeyMap created
10840          */
10841         addKeyMap : function(config){
10842             return new Roo.KeyMap(this, config);
10843         },
10844
10845         /**
10846          * Returns true if this element is scrollable.
10847          * @return {Boolean}
10848          */
10849          isScrollable : function(){
10850             var dom = this.dom;
10851             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10852         },
10853
10854         /**
10855          * 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().
10856          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10857          * @param {Number} value The new scroll value
10858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10859          * @return {Element} this
10860          */
10861
10862         scrollTo : function(side, value, animate){
10863             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10864             if(!animate || !A){
10865                 this.dom[prop] = value;
10866             }else{
10867                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10868                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10869             }
10870             return this;
10871         },
10872
10873         /**
10874          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10875          * within this element's scrollable range.
10876          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10877          * @param {Number} distance How far to scroll the element in pixels
10878          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10879          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10880          * was scrolled as far as it could go.
10881          */
10882          scroll : function(direction, distance, animate){
10883              if(!this.isScrollable()){
10884                  return;
10885              }
10886              var el = this.dom;
10887              var l = el.scrollLeft, t = el.scrollTop;
10888              var w = el.scrollWidth, h = el.scrollHeight;
10889              var cw = el.clientWidth, ch = el.clientHeight;
10890              direction = direction.toLowerCase();
10891              var scrolled = false;
10892              var a = this.preanim(arguments, 2);
10893              switch(direction){
10894                  case "l":
10895                  case "left":
10896                      if(w - l > cw){
10897                          var v = Math.min(l + distance, w-cw);
10898                          this.scrollTo("left", v, a);
10899                          scrolled = true;
10900                      }
10901                      break;
10902                 case "r":
10903                 case "right":
10904                      if(l > 0){
10905                          var v = Math.max(l - distance, 0);
10906                          this.scrollTo("left", v, a);
10907                          scrolled = true;
10908                      }
10909                      break;
10910                 case "t":
10911                 case "top":
10912                 case "up":
10913                      if(t > 0){
10914                          var v = Math.max(t - distance, 0);
10915                          this.scrollTo("top", v, a);
10916                          scrolled = true;
10917                      }
10918                      break;
10919                 case "b":
10920                 case "bottom":
10921                 case "down":
10922                      if(h - t > ch){
10923                          var v = Math.min(t + distance, h-ch);
10924                          this.scrollTo("top", v, a);
10925                          scrolled = true;
10926                      }
10927                      break;
10928              }
10929              return scrolled;
10930         },
10931
10932         /**
10933          * Translates the passed page coordinates into left/top css values for this element
10934          * @param {Number/Array} x The page x or an array containing [x, y]
10935          * @param {Number} y The page y
10936          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10937          */
10938         translatePoints : function(x, y){
10939             if(typeof x == 'object' || x instanceof Array){
10940                 y = x[1]; x = x[0];
10941             }
10942             var p = this.getStyle('position');
10943             var o = this.getXY();
10944
10945             var l = parseInt(this.getStyle('left'), 10);
10946             var t = parseInt(this.getStyle('top'), 10);
10947
10948             if(isNaN(l)){
10949                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10950             }
10951             if(isNaN(t)){
10952                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10953             }
10954
10955             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10956         },
10957
10958         /**
10959          * Returns the current scroll position of the element.
10960          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10961          */
10962         getScroll : function(){
10963             var d = this.dom, doc = document;
10964             if(d == doc || d == doc.body){
10965                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10966                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10967                 return {left: l, top: t};
10968             }else{
10969                 return {left: d.scrollLeft, top: d.scrollTop};
10970             }
10971         },
10972
10973         /**
10974          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10975          * are convert to standard 6 digit hex color.
10976          * @param {String} attr The css attribute
10977          * @param {String} defaultValue The default value to use when a valid color isn't found
10978          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10979          * YUI color anims.
10980          */
10981         getColor : function(attr, defaultValue, prefix){
10982             var v = this.getStyle(attr);
10983             if(!v || v == "transparent" || v == "inherit") {
10984                 return defaultValue;
10985             }
10986             var color = typeof prefix == "undefined" ? "#" : prefix;
10987             if(v.substr(0, 4) == "rgb("){
10988                 var rvs = v.slice(4, v.length -1).split(",");
10989                 for(var i = 0; i < 3; i++){
10990                     var h = parseInt(rvs[i]).toString(16);
10991                     if(h < 16){
10992                         h = "0" + h;
10993                     }
10994                     color += h;
10995                 }
10996             } else {
10997                 if(v.substr(0, 1) == "#"){
10998                     if(v.length == 4) {
10999                         for(var i = 1; i < 4; i++){
11000                             var c = v.charAt(i);
11001                             color +=  c + c;
11002                         }
11003                     }else if(v.length == 7){
11004                         color += v.substr(1);
11005                     }
11006                 }
11007             }
11008             return(color.length > 5 ? color.toLowerCase() : defaultValue);
11009         },
11010
11011         /**
11012          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
11013          * gradient background, rounded corners and a 4-way shadow.
11014          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
11015          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11016          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11017          * @return {Roo.Element} this
11018          */
11019         boxWrap : function(cls){
11020             cls = cls || 'x-box';
11021             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11022             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11023             return el;
11024         },
11025
11026         /**
11027          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11028          * @param {String} namespace The namespace in which to look for the attribute
11029          * @param {String} name The attribute name
11030          * @return {String} The attribute value
11031          */
11032         getAttributeNS : Roo.isIE ? function(ns, name){
11033             var d = this.dom;
11034             var type = typeof d[ns+":"+name];
11035             if(type != 'undefined' && type != 'unknown'){
11036                 return d[ns+":"+name];
11037             }
11038             return d[name];
11039         } : function(ns, name){
11040             var d = this.dom;
11041             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11042         },
11043         
11044         
11045         /**
11046          * Sets or Returns the value the dom attribute value
11047          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11048          * @param {String} value (optional) The value to set the attribute to
11049          * @return {String} The attribute value
11050          */
11051         attr : function(name){
11052             if (arguments.length > 1) {
11053                 this.dom.setAttribute(name, arguments[1]);
11054                 return arguments[1];
11055             }
11056             if (typeof(name) == 'object') {
11057                 for(var i in name) {
11058                     this.attr(i, name[i]);
11059                 }
11060                 return name;
11061             }
11062             
11063             
11064             if (!this.dom.hasAttribute(name)) {
11065                 return undefined;
11066             }
11067             return this.dom.getAttribute(name);
11068         }
11069         
11070         
11071         
11072     };
11073
11074     var ep = El.prototype;
11075
11076     /**
11077      * Appends an event handler (Shorthand for addListener)
11078      * @param {String}   eventName     The type of event to append
11079      * @param {Function} fn        The method the event invokes
11080      * @param {Object} scope       (optional) The scope (this object) of the fn
11081      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11082      * @method
11083      */
11084     ep.on = ep.addListener;
11085         // backwards compat
11086     ep.mon = ep.addListener;
11087
11088     /**
11089      * Removes an event handler from this element (shorthand for removeListener)
11090      * @param {String} eventName the type of event to remove
11091      * @param {Function} fn the method the event invokes
11092      * @return {Roo.Element} this
11093      * @method
11094      */
11095     ep.un = ep.removeListener;
11096
11097     /**
11098      * true to automatically adjust width and height settings for box-model issues (default to true)
11099      */
11100     ep.autoBoxAdjust = true;
11101
11102     // private
11103     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11104
11105     // private
11106     El.addUnits = function(v, defaultUnit){
11107         if(v === "" || v == "auto"){
11108             return v;
11109         }
11110         if(v === undefined){
11111             return '';
11112         }
11113         if(typeof v == "number" || !El.unitPattern.test(v)){
11114             return v + (defaultUnit || 'px');
11115         }
11116         return v;
11117     };
11118
11119     // special markup used throughout Roo when box wrapping elements
11120     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>';
11121     /**
11122      * Visibility mode constant - Use visibility to hide element
11123      * @static
11124      * @type Number
11125      */
11126     El.VISIBILITY = 1;
11127     /**
11128      * Visibility mode constant - Use display to hide element
11129      * @static
11130      * @type Number
11131      */
11132     El.DISPLAY = 2;
11133
11134     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11135     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11136     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11137
11138
11139
11140     /**
11141      * @private
11142      */
11143     El.cache = {};
11144
11145     var docEl;
11146
11147     /**
11148      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11149      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11150      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11151      * @return {Element} The Element object
11152      * @static
11153      */
11154     El.get = function(el){
11155         var ex, elm, id;
11156         if(!el){ return null; }
11157         if(typeof el == "string"){ // element id
11158             if(!(elm = document.getElementById(el))){
11159                 return null;
11160             }
11161             if(ex = El.cache[el]){
11162                 ex.dom = elm;
11163             }else{
11164                 ex = El.cache[el] = new El(elm);
11165             }
11166             return ex;
11167         }else if(el.tagName){ // dom element
11168             if(!(id = el.id)){
11169                 id = Roo.id(el);
11170             }
11171             if(ex = El.cache[id]){
11172                 ex.dom = el;
11173             }else{
11174                 ex = El.cache[id] = new El(el);
11175             }
11176             return ex;
11177         }else if(el instanceof El){
11178             if(el != docEl){
11179                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11180                                                               // catch case where it hasn't been appended
11181                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11182             }
11183             return el;
11184         }else if(el.isComposite){
11185             return el;
11186         }else if(el instanceof Array){
11187             return El.select(el);
11188         }else if(el == document){
11189             // create a bogus element object representing the document object
11190             if(!docEl){
11191                 var f = function(){};
11192                 f.prototype = El.prototype;
11193                 docEl = new f();
11194                 docEl.dom = document;
11195             }
11196             return docEl;
11197         }
11198         return null;
11199     };
11200
11201     // private
11202     El.uncache = function(el){
11203         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11204             if(a[i]){
11205                 delete El.cache[a[i].id || a[i]];
11206             }
11207         }
11208     };
11209
11210     // private
11211     // Garbage collection - uncache elements/purge listeners on orphaned elements
11212     // so we don't hold a reference and cause the browser to retain them
11213     El.garbageCollect = function(){
11214         if(!Roo.enableGarbageCollector){
11215             clearInterval(El.collectorThread);
11216             return;
11217         }
11218         for(var eid in El.cache){
11219             var el = El.cache[eid], d = el.dom;
11220             // -------------------------------------------------------
11221             // Determining what is garbage:
11222             // -------------------------------------------------------
11223             // !d
11224             // dom node is null, definitely garbage
11225             // -------------------------------------------------------
11226             // !d.parentNode
11227             // no parentNode == direct orphan, definitely garbage
11228             // -------------------------------------------------------
11229             // !d.offsetParent && !document.getElementById(eid)
11230             // display none elements have no offsetParent so we will
11231             // also try to look it up by it's id. However, check
11232             // offsetParent first so we don't do unneeded lookups.
11233             // This enables collection of elements that are not orphans
11234             // directly, but somewhere up the line they have an orphan
11235             // parent.
11236             // -------------------------------------------------------
11237             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11238                 delete El.cache[eid];
11239                 if(d && Roo.enableListenerCollection){
11240                     E.purgeElement(d);
11241                 }
11242             }
11243         }
11244     }
11245     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11246
11247
11248     // dom is optional
11249     El.Flyweight = function(dom){
11250         this.dom = dom;
11251     };
11252     El.Flyweight.prototype = El.prototype;
11253
11254     El._flyweights = {};
11255     /**
11256      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11257      * the dom node can be overwritten by other code.
11258      * @param {String/HTMLElement} el The dom node or id
11259      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11260      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11261      * @static
11262      * @return {Element} The shared Element object
11263      */
11264     El.fly = function(el, named){
11265         named = named || '_global';
11266         el = Roo.getDom(el);
11267         if(!el){
11268             return null;
11269         }
11270         if(!El._flyweights[named]){
11271             El._flyweights[named] = new El.Flyweight();
11272         }
11273         El._flyweights[named].dom = el;
11274         return El._flyweights[named];
11275     };
11276
11277     /**
11278      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11279      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11280      * Shorthand of {@link Roo.Element#get}
11281      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11282      * @return {Element} The Element object
11283      * @member Roo
11284      * @method get
11285      */
11286     Roo.get = El.get;
11287     /**
11288      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11289      * the dom node can be overwritten by other code.
11290      * Shorthand of {@link Roo.Element#fly}
11291      * @param {String/HTMLElement} el The dom node or id
11292      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11293      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11294      * @static
11295      * @return {Element} The shared Element object
11296      * @member Roo
11297      * @method fly
11298      */
11299     Roo.fly = El.fly;
11300
11301     // speedy lookup for elements never to box adjust
11302     var noBoxAdjust = Roo.isStrict ? {
11303         select:1
11304     } : {
11305         input:1, select:1, textarea:1
11306     };
11307     if(Roo.isIE || Roo.isGecko){
11308         noBoxAdjust['button'] = 1;
11309     }
11310
11311
11312     Roo.EventManager.on(window, 'unload', function(){
11313         delete El.cache;
11314         delete El._flyweights;
11315     });
11316 })();
11317
11318
11319
11320
11321 if(Roo.DomQuery){
11322     Roo.Element.selectorFunction = Roo.DomQuery.select;
11323 }
11324
11325 Roo.Element.select = function(selector, unique, root){
11326     var els;
11327     if(typeof selector == "string"){
11328         els = Roo.Element.selectorFunction(selector, root);
11329     }else if(selector.length !== undefined){
11330         els = selector;
11331     }else{
11332         throw "Invalid selector";
11333     }
11334     if(unique === true){
11335         return new Roo.CompositeElement(els);
11336     }else{
11337         return new Roo.CompositeElementLite(els);
11338     }
11339 };
11340 /**
11341  * Selects elements based on the passed CSS selector to enable working on them as 1.
11342  * @param {String/Array} selector The CSS selector or an array of elements
11343  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11344  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11345  * @return {CompositeElementLite/CompositeElement}
11346  * @member Roo
11347  * @method select
11348  */
11349 Roo.select = Roo.Element.select;
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364 /*
11365  * Based on:
11366  * Ext JS Library 1.1.1
11367  * Copyright(c) 2006-2007, Ext JS, LLC.
11368  *
11369  * Originally Released Under LGPL - original licence link has changed is not relivant.
11370  *
11371  * Fork - LGPL
11372  * <script type="text/javascript">
11373  */
11374
11375
11376
11377 //Notifies Element that fx methods are available
11378 Roo.enableFx = true;
11379
11380 /**
11381  * @class Roo.Fx
11382  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11383  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11384  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11385  * Element effects to work.</p><br/>
11386  *
11387  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11388  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11389  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11390  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11391  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11392  * expected results and should be done with care.</p><br/>
11393  *
11394  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11395  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11396 <pre>
11397 Value  Description
11398 -----  -----------------------------
11399 tl     The top left corner
11400 t      The center of the top edge
11401 tr     The top right corner
11402 l      The center of the left edge
11403 r      The center of the right edge
11404 bl     The bottom left corner
11405 b      The center of the bottom edge
11406 br     The bottom right corner
11407 </pre>
11408  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11409  * below are common options that can be passed to any Fx method.</b>
11410  * @cfg {Function} callback A function called when the effect is finished
11411  * @cfg {Object} scope The scope of the effect function
11412  * @cfg {String} easing A valid Easing value for the effect
11413  * @cfg {String} afterCls A css class to apply after the effect
11414  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11415  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11416  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11417  * effects that end with the element being visually hidden, ignored otherwise)
11418  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11419  * a function which returns such a specification that will be applied to the Element after the effect finishes
11420  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11421  * @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
11422  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11423  */
11424 Roo.Fx = {
11425         /**
11426          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11427          * origin for the slide effect.  This function automatically handles wrapping the element with
11428          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11429          * Usage:
11430          *<pre><code>
11431 // default: slide the element in from the top
11432 el.slideIn();
11433
11434 // custom: slide the element in from the right with a 2-second duration
11435 el.slideIn('r', { duration: 2 });
11436
11437 // common config options shown with default values
11438 el.slideIn('t', {
11439     easing: 'easeOut',
11440     duration: .5
11441 });
11442 </code></pre>
11443          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11444          * @param {Object} options (optional) Object literal with any of the Fx config options
11445          * @return {Roo.Element} The Element
11446          */
11447     slideIn : function(anchor, o){
11448         var el = this.getFxEl();
11449         o = o || {};
11450
11451         el.queueFx(o, function(){
11452
11453             anchor = anchor || "t";
11454
11455             // fix display to visibility
11456             this.fixDisplay();
11457
11458             // restore values after effect
11459             var r = this.getFxRestore();
11460             var b = this.getBox();
11461             // fixed size for slide
11462             this.setSize(b);
11463
11464             // wrap if needed
11465             var wrap = this.fxWrap(r.pos, o, "hidden");
11466
11467             var st = this.dom.style;
11468             st.visibility = "visible";
11469             st.position = "absolute";
11470
11471             // clear out temp styles after slide and unwrap
11472             var after = function(){
11473                 el.fxUnwrap(wrap, r.pos, o);
11474                 st.width = r.width;
11475                 st.height = r.height;
11476                 el.afterFx(o);
11477             };
11478             // time to calc the positions
11479             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11480
11481             switch(anchor.toLowerCase()){
11482                 case "t":
11483                     wrap.setSize(b.width, 0);
11484                     st.left = st.bottom = "0";
11485                     a = {height: bh};
11486                 break;
11487                 case "l":
11488                     wrap.setSize(0, b.height);
11489                     st.right = st.top = "0";
11490                     a = {width: bw};
11491                 break;
11492                 case "r":
11493                     wrap.setSize(0, b.height);
11494                     wrap.setX(b.right);
11495                     st.left = st.top = "0";
11496                     a = {width: bw, points: pt};
11497                 break;
11498                 case "b":
11499                     wrap.setSize(b.width, 0);
11500                     wrap.setY(b.bottom);
11501                     st.left = st.top = "0";
11502                     a = {height: bh, points: pt};
11503                 break;
11504                 case "tl":
11505                     wrap.setSize(0, 0);
11506                     st.right = st.bottom = "0";
11507                     a = {width: bw, height: bh};
11508                 break;
11509                 case "bl":
11510                     wrap.setSize(0, 0);
11511                     wrap.setY(b.y+b.height);
11512                     st.right = st.top = "0";
11513                     a = {width: bw, height: bh, points: pt};
11514                 break;
11515                 case "br":
11516                     wrap.setSize(0, 0);
11517                     wrap.setXY([b.right, b.bottom]);
11518                     st.left = st.top = "0";
11519                     a = {width: bw, height: bh, points: pt};
11520                 break;
11521                 case "tr":
11522                     wrap.setSize(0, 0);
11523                     wrap.setX(b.x+b.width);
11524                     st.left = st.bottom = "0";
11525                     a = {width: bw, height: bh, points: pt};
11526                 break;
11527             }
11528             this.dom.style.visibility = "visible";
11529             wrap.show();
11530
11531             arguments.callee.anim = wrap.fxanim(a,
11532                 o,
11533                 'motion',
11534                 .5,
11535                 'easeOut', after);
11536         });
11537         return this;
11538     },
11539     
11540         /**
11541          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11542          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11543          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11544          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11545          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11546          * Usage:
11547          *<pre><code>
11548 // default: slide the element out to the top
11549 el.slideOut();
11550
11551 // custom: slide the element out to the right with a 2-second duration
11552 el.slideOut('r', { duration: 2 });
11553
11554 // common config options shown with default values
11555 el.slideOut('t', {
11556     easing: 'easeOut',
11557     duration: .5,
11558     remove: false,
11559     useDisplay: false
11560 });
11561 </code></pre>
11562          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11563          * @param {Object} options (optional) Object literal with any of the Fx config options
11564          * @return {Roo.Element} The Element
11565          */
11566     slideOut : function(anchor, o){
11567         var el = this.getFxEl();
11568         o = o || {};
11569
11570         el.queueFx(o, function(){
11571
11572             anchor = anchor || "t";
11573
11574             // restore values after effect
11575             var r = this.getFxRestore();
11576             
11577             var b = this.getBox();
11578             // fixed size for slide
11579             this.setSize(b);
11580
11581             // wrap if needed
11582             var wrap = this.fxWrap(r.pos, o, "visible");
11583
11584             var st = this.dom.style;
11585             st.visibility = "visible";
11586             st.position = "absolute";
11587
11588             wrap.setSize(b);
11589
11590             var after = function(){
11591                 if(o.useDisplay){
11592                     el.setDisplayed(false);
11593                 }else{
11594                     el.hide();
11595                 }
11596
11597                 el.fxUnwrap(wrap, r.pos, o);
11598
11599                 st.width = r.width;
11600                 st.height = r.height;
11601
11602                 el.afterFx(o);
11603             };
11604
11605             var a, zero = {to: 0};
11606             switch(anchor.toLowerCase()){
11607                 case "t":
11608                     st.left = st.bottom = "0";
11609                     a = {height: zero};
11610                 break;
11611                 case "l":
11612                     st.right = st.top = "0";
11613                     a = {width: zero};
11614                 break;
11615                 case "r":
11616                     st.left = st.top = "0";
11617                     a = {width: zero, points: {to:[b.right, b.y]}};
11618                 break;
11619                 case "b":
11620                     st.left = st.top = "0";
11621                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11622                 break;
11623                 case "tl":
11624                     st.right = st.bottom = "0";
11625                     a = {width: zero, height: zero};
11626                 break;
11627                 case "bl":
11628                     st.right = st.top = "0";
11629                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11630                 break;
11631                 case "br":
11632                     st.left = st.top = "0";
11633                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11634                 break;
11635                 case "tr":
11636                     st.left = st.bottom = "0";
11637                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11638                 break;
11639             }
11640
11641             arguments.callee.anim = wrap.fxanim(a,
11642                 o,
11643                 'motion',
11644                 .5,
11645                 "easeOut", after);
11646         });
11647         return this;
11648     },
11649
11650         /**
11651          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11652          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11653          * The element must be removed from the DOM using the 'remove' config option if desired.
11654          * Usage:
11655          *<pre><code>
11656 // default
11657 el.puff();
11658
11659 // common config options shown with default values
11660 el.puff({
11661     easing: 'easeOut',
11662     duration: .5,
11663     remove: false,
11664     useDisplay: false
11665 });
11666 </code></pre>
11667          * @param {Object} options (optional) Object literal with any of the Fx config options
11668          * @return {Roo.Element} The Element
11669          */
11670     puff : function(o){
11671         var el = this.getFxEl();
11672         o = o || {};
11673
11674         el.queueFx(o, function(){
11675             this.clearOpacity();
11676             this.show();
11677
11678             // restore values after effect
11679             var r = this.getFxRestore();
11680             var st = this.dom.style;
11681
11682             var after = function(){
11683                 if(o.useDisplay){
11684                     el.setDisplayed(false);
11685                 }else{
11686                     el.hide();
11687                 }
11688
11689                 el.clearOpacity();
11690
11691                 el.setPositioning(r.pos);
11692                 st.width = r.width;
11693                 st.height = r.height;
11694                 st.fontSize = '';
11695                 el.afterFx(o);
11696             };
11697
11698             var width = this.getWidth();
11699             var height = this.getHeight();
11700
11701             arguments.callee.anim = this.fxanim({
11702                     width : {to: this.adjustWidth(width * 2)},
11703                     height : {to: this.adjustHeight(height * 2)},
11704                     points : {by: [-(width * .5), -(height * .5)]},
11705                     opacity : {to: 0},
11706                     fontSize: {to:200, unit: "%"}
11707                 },
11708                 o,
11709                 'motion',
11710                 .5,
11711                 "easeOut", after);
11712         });
11713         return this;
11714     },
11715
11716         /**
11717          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11718          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11719          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11720          * Usage:
11721          *<pre><code>
11722 // default
11723 el.switchOff();
11724
11725 // all config options shown with default values
11726 el.switchOff({
11727     easing: 'easeIn',
11728     duration: .3,
11729     remove: false,
11730     useDisplay: false
11731 });
11732 </code></pre>
11733          * @param {Object} options (optional) Object literal with any of the Fx config options
11734          * @return {Roo.Element} The Element
11735          */
11736     switchOff : function(o){
11737         var el = this.getFxEl();
11738         o = o || {};
11739
11740         el.queueFx(o, function(){
11741             this.clearOpacity();
11742             this.clip();
11743
11744             // restore values after effect
11745             var r = this.getFxRestore();
11746             var st = this.dom.style;
11747
11748             var after = function(){
11749                 if(o.useDisplay){
11750                     el.setDisplayed(false);
11751                 }else{
11752                     el.hide();
11753                 }
11754
11755                 el.clearOpacity();
11756                 el.setPositioning(r.pos);
11757                 st.width = r.width;
11758                 st.height = r.height;
11759
11760                 el.afterFx(o);
11761             };
11762
11763             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11764                 this.clearOpacity();
11765                 (function(){
11766                     this.fxanim({
11767                         height:{to:1},
11768                         points:{by:[0, this.getHeight() * .5]}
11769                     }, o, 'motion', 0.3, 'easeIn', after);
11770                 }).defer(100, this);
11771             });
11772         });
11773         return this;
11774     },
11775
11776     /**
11777      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11778      * changed using the "attr" config option) and then fading back to the original color. If no original
11779      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11780      * Usage:
11781 <pre><code>
11782 // default: highlight background to yellow
11783 el.highlight();
11784
11785 // custom: highlight foreground text to blue for 2 seconds
11786 el.highlight("0000ff", { attr: 'color', duration: 2 });
11787
11788 // common config options shown with default values
11789 el.highlight("ffff9c", {
11790     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11791     endColor: (current color) or "ffffff",
11792     easing: 'easeIn',
11793     duration: 1
11794 });
11795 </code></pre>
11796      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11797      * @param {Object} options (optional) Object literal with any of the Fx config options
11798      * @return {Roo.Element} The Element
11799      */ 
11800     highlight : function(color, o){
11801         var el = this.getFxEl();
11802         o = o || {};
11803
11804         el.queueFx(o, function(){
11805             color = color || "ffff9c";
11806             attr = o.attr || "backgroundColor";
11807
11808             this.clearOpacity();
11809             this.show();
11810
11811             var origColor = this.getColor(attr);
11812             var restoreColor = this.dom.style[attr];
11813             endColor = (o.endColor || origColor) || "ffffff";
11814
11815             var after = function(){
11816                 el.dom.style[attr] = restoreColor;
11817                 el.afterFx(o);
11818             };
11819
11820             var a = {};
11821             a[attr] = {from: color, to: endColor};
11822             arguments.callee.anim = this.fxanim(a,
11823                 o,
11824                 'color',
11825                 1,
11826                 'easeIn', after);
11827         });
11828         return this;
11829     },
11830
11831    /**
11832     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11833     * Usage:
11834 <pre><code>
11835 // default: a single light blue ripple
11836 el.frame();
11837
11838 // custom: 3 red ripples lasting 3 seconds total
11839 el.frame("ff0000", 3, { duration: 3 });
11840
11841 // common config options shown with default values
11842 el.frame("C3DAF9", 1, {
11843     duration: 1 //duration of entire animation (not each individual ripple)
11844     // Note: Easing is not configurable and will be ignored if included
11845 });
11846 </code></pre>
11847     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11848     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11849     * @param {Object} options (optional) Object literal with any of the Fx config options
11850     * @return {Roo.Element} The Element
11851     */
11852     frame : function(color, count, o){
11853         var el = this.getFxEl();
11854         o = o || {};
11855
11856         el.queueFx(o, function(){
11857             color = color || "#C3DAF9";
11858             if(color.length == 6){
11859                 color = "#" + color;
11860             }
11861             count = count || 1;
11862             duration = o.duration || 1;
11863             this.show();
11864
11865             var b = this.getBox();
11866             var animFn = function(){
11867                 var proxy = this.createProxy({
11868
11869                      style:{
11870                         visbility:"hidden",
11871                         position:"absolute",
11872                         "z-index":"35000", // yee haw
11873                         border:"0px solid " + color
11874                      }
11875                   });
11876                 var scale = Roo.isBorderBox ? 2 : 1;
11877                 proxy.animate({
11878                     top:{from:b.y, to:b.y - 20},
11879                     left:{from:b.x, to:b.x - 20},
11880                     borderWidth:{from:0, to:10},
11881                     opacity:{from:1, to:0},
11882                     height:{from:b.height, to:(b.height + (20*scale))},
11883                     width:{from:b.width, to:(b.width + (20*scale))}
11884                 }, duration, function(){
11885                     proxy.remove();
11886                 });
11887                 if(--count > 0){
11888                      animFn.defer((duration/2)*1000, this);
11889                 }else{
11890                     el.afterFx(o);
11891                 }
11892             };
11893             animFn.call(this);
11894         });
11895         return this;
11896     },
11897
11898    /**
11899     * Creates a pause before any subsequent queued effects begin.  If there are
11900     * no effects queued after the pause it will have no effect.
11901     * Usage:
11902 <pre><code>
11903 el.pause(1);
11904 </code></pre>
11905     * @param {Number} seconds The length of time to pause (in seconds)
11906     * @return {Roo.Element} The Element
11907     */
11908     pause : function(seconds){
11909         var el = this.getFxEl();
11910         var o = {};
11911
11912         el.queueFx(o, function(){
11913             setTimeout(function(){
11914                 el.afterFx(o);
11915             }, seconds * 1000);
11916         });
11917         return this;
11918     },
11919
11920    /**
11921     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11922     * using the "endOpacity" config option.
11923     * Usage:
11924 <pre><code>
11925 // default: fade in from opacity 0 to 100%
11926 el.fadeIn();
11927
11928 // custom: fade in from opacity 0 to 75% over 2 seconds
11929 el.fadeIn({ endOpacity: .75, duration: 2});
11930
11931 // common config options shown with default values
11932 el.fadeIn({
11933     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11934     easing: 'easeOut',
11935     duration: .5
11936 });
11937 </code></pre>
11938     * @param {Object} options (optional) Object literal with any of the Fx config options
11939     * @return {Roo.Element} The Element
11940     */
11941     fadeIn : function(o){
11942         var el = this.getFxEl();
11943         o = o || {};
11944         el.queueFx(o, function(){
11945             this.setOpacity(0);
11946             this.fixDisplay();
11947             this.dom.style.visibility = 'visible';
11948             var to = o.endOpacity || 1;
11949             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11950                 o, null, .5, "easeOut", function(){
11951                 if(to == 1){
11952                     this.clearOpacity();
11953                 }
11954                 el.afterFx(o);
11955             });
11956         });
11957         return this;
11958     },
11959
11960    /**
11961     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11962     * using the "endOpacity" config option.
11963     * Usage:
11964 <pre><code>
11965 // default: fade out from the element's current opacity to 0
11966 el.fadeOut();
11967
11968 // custom: fade out from the element's current opacity to 25% over 2 seconds
11969 el.fadeOut({ endOpacity: .25, duration: 2});
11970
11971 // common config options shown with default values
11972 el.fadeOut({
11973     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11974     easing: 'easeOut',
11975     duration: .5
11976     remove: false,
11977     useDisplay: false
11978 });
11979 </code></pre>
11980     * @param {Object} options (optional) Object literal with any of the Fx config options
11981     * @return {Roo.Element} The Element
11982     */
11983     fadeOut : function(o){
11984         var el = this.getFxEl();
11985         o = o || {};
11986         el.queueFx(o, function(){
11987             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11988                 o, null, .5, "easeOut", function(){
11989                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11990                      this.dom.style.display = "none";
11991                 }else{
11992                      this.dom.style.visibility = "hidden";
11993                 }
11994                 this.clearOpacity();
11995                 el.afterFx(o);
11996             });
11997         });
11998         return this;
11999     },
12000
12001    /**
12002     * Animates the transition of an element's dimensions from a starting height/width
12003     * to an ending height/width.
12004     * Usage:
12005 <pre><code>
12006 // change height and width to 100x100 pixels
12007 el.scale(100, 100);
12008
12009 // common config options shown with default values.  The height and width will default to
12010 // the element's existing values if passed as null.
12011 el.scale(
12012     [element's width],
12013     [element's height], {
12014     easing: 'easeOut',
12015     duration: .35
12016 });
12017 </code></pre>
12018     * @param {Number} width  The new width (pass undefined to keep the original width)
12019     * @param {Number} height  The new height (pass undefined to keep the original height)
12020     * @param {Object} options (optional) Object literal with any of the Fx config options
12021     * @return {Roo.Element} The Element
12022     */
12023     scale : function(w, h, o){
12024         this.shift(Roo.apply({}, o, {
12025             width: w,
12026             height: h
12027         }));
12028         return this;
12029     },
12030
12031    /**
12032     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12033     * Any of these properties not specified in the config object will not be changed.  This effect 
12034     * requires that at least one new dimension, position or opacity setting must be passed in on
12035     * the config object in order for the function to have any effect.
12036     * Usage:
12037 <pre><code>
12038 // slide the element horizontally to x position 200 while changing the height and opacity
12039 el.shift({ x: 200, height: 50, opacity: .8 });
12040
12041 // common config options shown with default values.
12042 el.shift({
12043     width: [element's width],
12044     height: [element's height],
12045     x: [element's x position],
12046     y: [element's y position],
12047     opacity: [element's opacity],
12048     easing: 'easeOut',
12049     duration: .35
12050 });
12051 </code></pre>
12052     * @param {Object} options  Object literal with any of the Fx config options
12053     * @return {Roo.Element} The Element
12054     */
12055     shift : function(o){
12056         var el = this.getFxEl();
12057         o = o || {};
12058         el.queueFx(o, function(){
12059             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12060             if(w !== undefined){
12061                 a.width = {to: this.adjustWidth(w)};
12062             }
12063             if(h !== undefined){
12064                 a.height = {to: this.adjustHeight(h)};
12065             }
12066             if(x !== undefined || y !== undefined){
12067                 a.points = {to: [
12068                     x !== undefined ? x : this.getX(),
12069                     y !== undefined ? y : this.getY()
12070                 ]};
12071             }
12072             if(op !== undefined){
12073                 a.opacity = {to: op};
12074             }
12075             if(o.xy !== undefined){
12076                 a.points = {to: o.xy};
12077             }
12078             arguments.callee.anim = this.fxanim(a,
12079                 o, 'motion', .35, "easeOut", function(){
12080                 el.afterFx(o);
12081             });
12082         });
12083         return this;
12084     },
12085
12086         /**
12087          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12088          * ending point of the effect.
12089          * Usage:
12090          *<pre><code>
12091 // default: slide the element downward while fading out
12092 el.ghost();
12093
12094 // custom: slide the element out to the right with a 2-second duration
12095 el.ghost('r', { duration: 2 });
12096
12097 // common config options shown with default values
12098 el.ghost('b', {
12099     easing: 'easeOut',
12100     duration: .5
12101     remove: false,
12102     useDisplay: false
12103 });
12104 </code></pre>
12105          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12106          * @param {Object} options (optional) Object literal with any of the Fx config options
12107          * @return {Roo.Element} The Element
12108          */
12109     ghost : function(anchor, o){
12110         var el = this.getFxEl();
12111         o = o || {};
12112
12113         el.queueFx(o, function(){
12114             anchor = anchor || "b";
12115
12116             // restore values after effect
12117             var r = this.getFxRestore();
12118             var w = this.getWidth(),
12119                 h = this.getHeight();
12120
12121             var st = this.dom.style;
12122
12123             var after = function(){
12124                 if(o.useDisplay){
12125                     el.setDisplayed(false);
12126                 }else{
12127                     el.hide();
12128                 }
12129
12130                 el.clearOpacity();
12131                 el.setPositioning(r.pos);
12132                 st.width = r.width;
12133                 st.height = r.height;
12134
12135                 el.afterFx(o);
12136             };
12137
12138             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12139             switch(anchor.toLowerCase()){
12140                 case "t":
12141                     pt.by = [0, -h];
12142                 break;
12143                 case "l":
12144                     pt.by = [-w, 0];
12145                 break;
12146                 case "r":
12147                     pt.by = [w, 0];
12148                 break;
12149                 case "b":
12150                     pt.by = [0, h];
12151                 break;
12152                 case "tl":
12153                     pt.by = [-w, -h];
12154                 break;
12155                 case "bl":
12156                     pt.by = [-w, h];
12157                 break;
12158                 case "br":
12159                     pt.by = [w, h];
12160                 break;
12161                 case "tr":
12162                     pt.by = [w, -h];
12163                 break;
12164             }
12165
12166             arguments.callee.anim = this.fxanim(a,
12167                 o,
12168                 'motion',
12169                 .5,
12170                 "easeOut", after);
12171         });
12172         return this;
12173     },
12174
12175         /**
12176          * Ensures that all effects queued after syncFx is called on the element are
12177          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12178          * @return {Roo.Element} The Element
12179          */
12180     syncFx : function(){
12181         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12182             block : false,
12183             concurrent : true,
12184             stopFx : false
12185         });
12186         return this;
12187     },
12188
12189         /**
12190          * Ensures that all effects queued after sequenceFx is called on the element are
12191          * run in sequence.  This is the opposite of {@link #syncFx}.
12192          * @return {Roo.Element} The Element
12193          */
12194     sequenceFx : function(){
12195         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12196             block : false,
12197             concurrent : false,
12198             stopFx : false
12199         });
12200         return this;
12201     },
12202
12203         /* @private */
12204     nextFx : function(){
12205         var ef = this.fxQueue[0];
12206         if(ef){
12207             ef.call(this);
12208         }
12209     },
12210
12211         /**
12212          * Returns true if the element has any effects actively running or queued, else returns false.
12213          * @return {Boolean} True if element has active effects, else false
12214          */
12215     hasActiveFx : function(){
12216         return this.fxQueue && this.fxQueue[0];
12217     },
12218
12219         /**
12220          * Stops any running effects and clears the element's internal effects queue if it contains
12221          * any additional effects that haven't started yet.
12222          * @return {Roo.Element} The Element
12223          */
12224     stopFx : function(){
12225         if(this.hasActiveFx()){
12226             var cur = this.fxQueue[0];
12227             if(cur && cur.anim && cur.anim.isAnimated()){
12228                 this.fxQueue = [cur]; // clear out others
12229                 cur.anim.stop(true);
12230             }
12231         }
12232         return this;
12233     },
12234
12235         /* @private */
12236     beforeFx : function(o){
12237         if(this.hasActiveFx() && !o.concurrent){
12238            if(o.stopFx){
12239                this.stopFx();
12240                return true;
12241            }
12242            return false;
12243         }
12244         return true;
12245     },
12246
12247         /**
12248          * Returns true if the element is currently blocking so that no other effect can be queued
12249          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12250          * used to ensure that an effect initiated by a user action runs to completion prior to the
12251          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12252          * @return {Boolean} True if blocking, else false
12253          */
12254     hasFxBlock : function(){
12255         var q = this.fxQueue;
12256         return q && q[0] && q[0].block;
12257     },
12258
12259         /* @private */
12260     queueFx : function(o, fn){
12261         if(!this.fxQueue){
12262             this.fxQueue = [];
12263         }
12264         if(!this.hasFxBlock()){
12265             Roo.applyIf(o, this.fxDefaults);
12266             if(!o.concurrent){
12267                 var run = this.beforeFx(o);
12268                 fn.block = o.block;
12269                 this.fxQueue.push(fn);
12270                 if(run){
12271                     this.nextFx();
12272                 }
12273             }else{
12274                 fn.call(this);
12275             }
12276         }
12277         return this;
12278     },
12279
12280         /* @private */
12281     fxWrap : function(pos, o, vis){
12282         var wrap;
12283         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12284             var wrapXY;
12285             if(o.fixPosition){
12286                 wrapXY = this.getXY();
12287             }
12288             var div = document.createElement("div");
12289             div.style.visibility = vis;
12290             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12291             wrap.setPositioning(pos);
12292             if(wrap.getStyle("position") == "static"){
12293                 wrap.position("relative");
12294             }
12295             this.clearPositioning('auto');
12296             wrap.clip();
12297             wrap.dom.appendChild(this.dom);
12298             if(wrapXY){
12299                 wrap.setXY(wrapXY);
12300             }
12301         }
12302         return wrap;
12303     },
12304
12305         /* @private */
12306     fxUnwrap : function(wrap, pos, o){
12307         this.clearPositioning();
12308         this.setPositioning(pos);
12309         if(!o.wrap){
12310             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12311             wrap.remove();
12312         }
12313     },
12314
12315         /* @private */
12316     getFxRestore : function(){
12317         var st = this.dom.style;
12318         return {pos: this.getPositioning(), width: st.width, height : st.height};
12319     },
12320
12321         /* @private */
12322     afterFx : function(o){
12323         if(o.afterStyle){
12324             this.applyStyles(o.afterStyle);
12325         }
12326         if(o.afterCls){
12327             this.addClass(o.afterCls);
12328         }
12329         if(o.remove === true){
12330             this.remove();
12331         }
12332         Roo.callback(o.callback, o.scope, [this]);
12333         if(!o.concurrent){
12334             this.fxQueue.shift();
12335             this.nextFx();
12336         }
12337     },
12338
12339         /* @private */
12340     getFxEl : function(){ // support for composite element fx
12341         return Roo.get(this.dom);
12342     },
12343
12344         /* @private */
12345     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12346         animType = animType || 'run';
12347         opt = opt || {};
12348         var anim = Roo.lib.Anim[animType](
12349             this.dom, args,
12350             (opt.duration || defaultDur) || .35,
12351             (opt.easing || defaultEase) || 'easeOut',
12352             function(){
12353                 Roo.callback(cb, this);
12354             },
12355             this
12356         );
12357         opt.anim = anim;
12358         return anim;
12359     }
12360 };
12361
12362 // backwords compat
12363 Roo.Fx.resize = Roo.Fx.scale;
12364
12365 //When included, Roo.Fx is automatically applied to Element so that all basic
12366 //effects are available directly via the Element API
12367 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12368  * Based on:
12369  * Ext JS Library 1.1.1
12370  * Copyright(c) 2006-2007, Ext JS, LLC.
12371  *
12372  * Originally Released Under LGPL - original licence link has changed is not relivant.
12373  *
12374  * Fork - LGPL
12375  * <script type="text/javascript">
12376  */
12377
12378
12379 /**
12380  * @class Roo.CompositeElement
12381  * Standard composite class. Creates a Roo.Element for every element in the collection.
12382  * <br><br>
12383  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12384  * actions will be performed on all the elements in this collection.</b>
12385  * <br><br>
12386  * All methods return <i>this</i> and can be chained.
12387  <pre><code>
12388  var els = Roo.select("#some-el div.some-class", true);
12389  // or select directly from an existing element
12390  var el = Roo.get('some-el');
12391  el.select('div.some-class', true);
12392
12393  els.setWidth(100); // all elements become 100 width
12394  els.hide(true); // all elements fade out and hide
12395  // or
12396  els.setWidth(100).hide(true);
12397  </code></pre>
12398  */
12399 Roo.CompositeElement = function(els){
12400     this.elements = [];
12401     this.addElements(els);
12402 };
12403 Roo.CompositeElement.prototype = {
12404     isComposite: true,
12405     addElements : function(els){
12406         if(!els) {
12407             return this;
12408         }
12409         if(typeof els == "string"){
12410             els = Roo.Element.selectorFunction(els);
12411         }
12412         var yels = this.elements;
12413         var index = yels.length-1;
12414         for(var i = 0, len = els.length; i < len; i++) {
12415                 yels[++index] = Roo.get(els[i]);
12416         }
12417         return this;
12418     },
12419
12420     /**
12421     * Clears this composite and adds the elements returned by the passed selector.
12422     * @param {String/Array} els A string CSS selector, an array of elements or an element
12423     * @return {CompositeElement} this
12424     */
12425     fill : function(els){
12426         this.elements = [];
12427         this.add(els);
12428         return this;
12429     },
12430
12431     /**
12432     * Filters this composite to only elements that match the passed selector.
12433     * @param {String} selector A string CSS selector
12434     * @param {Boolean} inverse return inverse filter (not matches)
12435     * @return {CompositeElement} this
12436     */
12437     filter : function(selector, inverse){
12438         var els = [];
12439         inverse = inverse || false;
12440         this.each(function(el){
12441             var match = inverse ? !el.is(selector) : el.is(selector);
12442             if(match){
12443                 els[els.length] = el.dom;
12444             }
12445         });
12446         this.fill(els);
12447         return this;
12448     },
12449
12450     invoke : function(fn, args){
12451         var els = this.elements;
12452         for(var i = 0, len = els.length; i < len; i++) {
12453                 Roo.Element.prototype[fn].apply(els[i], args);
12454         }
12455         return this;
12456     },
12457     /**
12458     * Adds elements to this composite.
12459     * @param {String/Array} els A string CSS selector, an array of elements or an element
12460     * @return {CompositeElement} this
12461     */
12462     add : function(els){
12463         if(typeof els == "string"){
12464             this.addElements(Roo.Element.selectorFunction(els));
12465         }else if(els.length !== undefined){
12466             this.addElements(els);
12467         }else{
12468             this.addElements([els]);
12469         }
12470         return this;
12471     },
12472     /**
12473     * Calls the passed function passing (el, this, index) for each element in this composite.
12474     * @param {Function} fn The function to call
12475     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12476     * @return {CompositeElement} this
12477     */
12478     each : function(fn, scope){
12479         var els = this.elements;
12480         for(var i = 0, len = els.length; i < len; i++){
12481             if(fn.call(scope || els[i], els[i], this, i) === false) {
12482                 break;
12483             }
12484         }
12485         return this;
12486     },
12487
12488     /**
12489      * Returns the Element object at the specified index
12490      * @param {Number} index
12491      * @return {Roo.Element}
12492      */
12493     item : function(index){
12494         return this.elements[index] || null;
12495     },
12496
12497     /**
12498      * Returns the first Element
12499      * @return {Roo.Element}
12500      */
12501     first : function(){
12502         return this.item(0);
12503     },
12504
12505     /**
12506      * Returns the last Element
12507      * @return {Roo.Element}
12508      */
12509     last : function(){
12510         return this.item(this.elements.length-1);
12511     },
12512
12513     /**
12514      * Returns the number of elements in this composite
12515      * @return Number
12516      */
12517     getCount : function(){
12518         return this.elements.length;
12519     },
12520
12521     /**
12522      * Returns true if this composite contains the passed element
12523      * @return Boolean
12524      */
12525     contains : function(el){
12526         return this.indexOf(el) !== -1;
12527     },
12528
12529     /**
12530      * Returns true if this composite contains the passed element
12531      * @return Boolean
12532      */
12533     indexOf : function(el){
12534         return this.elements.indexOf(Roo.get(el));
12535     },
12536
12537
12538     /**
12539     * Removes the specified element(s).
12540     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12541     * or an array of any of those.
12542     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12543     * @return {CompositeElement} this
12544     */
12545     removeElement : function(el, removeDom){
12546         if(el instanceof Array){
12547             for(var i = 0, len = el.length; i < len; i++){
12548                 this.removeElement(el[i]);
12549             }
12550             return this;
12551         }
12552         var index = typeof el == 'number' ? el : this.indexOf(el);
12553         if(index !== -1){
12554             if(removeDom){
12555                 var d = this.elements[index];
12556                 if(d.dom){
12557                     d.remove();
12558                 }else{
12559                     d.parentNode.removeChild(d);
12560                 }
12561             }
12562             this.elements.splice(index, 1);
12563         }
12564         return this;
12565     },
12566
12567     /**
12568     * Replaces the specified element with the passed element.
12569     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12570     * to replace.
12571     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12572     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12573     * @return {CompositeElement} this
12574     */
12575     replaceElement : function(el, replacement, domReplace){
12576         var index = typeof el == 'number' ? el : this.indexOf(el);
12577         if(index !== -1){
12578             if(domReplace){
12579                 this.elements[index].replaceWith(replacement);
12580             }else{
12581                 this.elements.splice(index, 1, Roo.get(replacement))
12582             }
12583         }
12584         return this;
12585     },
12586
12587     /**
12588      * Removes all elements.
12589      */
12590     clear : function(){
12591         this.elements = [];
12592     }
12593 };
12594 (function(){
12595     Roo.CompositeElement.createCall = function(proto, fnName){
12596         if(!proto[fnName]){
12597             proto[fnName] = function(){
12598                 return this.invoke(fnName, arguments);
12599             };
12600         }
12601     };
12602     for(var fnName in Roo.Element.prototype){
12603         if(typeof Roo.Element.prototype[fnName] == "function"){
12604             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12605         }
12606     };
12607 })();
12608 /*
12609  * Based on:
12610  * Ext JS Library 1.1.1
12611  * Copyright(c) 2006-2007, Ext JS, LLC.
12612  *
12613  * Originally Released Under LGPL - original licence link has changed is not relivant.
12614  *
12615  * Fork - LGPL
12616  * <script type="text/javascript">
12617  */
12618
12619 /**
12620  * @class Roo.CompositeElementLite
12621  * @extends Roo.CompositeElement
12622  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12623  <pre><code>
12624  var els = Roo.select("#some-el div.some-class");
12625  // or select directly from an existing element
12626  var el = Roo.get('some-el');
12627  el.select('div.some-class');
12628
12629  els.setWidth(100); // all elements become 100 width
12630  els.hide(true); // all elements fade out and hide
12631  // or
12632  els.setWidth(100).hide(true);
12633  </code></pre><br><br>
12634  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12635  * actions will be performed on all the elements in this collection.</b>
12636  */
12637 Roo.CompositeElementLite = function(els){
12638     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12639     this.el = new Roo.Element.Flyweight();
12640 };
12641 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12642     addElements : function(els){
12643         if(els){
12644             if(els instanceof Array){
12645                 this.elements = this.elements.concat(els);
12646             }else{
12647                 var yels = this.elements;
12648                 var index = yels.length-1;
12649                 for(var i = 0, len = els.length; i < len; i++) {
12650                     yels[++index] = els[i];
12651                 }
12652             }
12653         }
12654         return this;
12655     },
12656     invoke : function(fn, args){
12657         var els = this.elements;
12658         var el = this.el;
12659         for(var i = 0, len = els.length; i < len; i++) {
12660             el.dom = els[i];
12661                 Roo.Element.prototype[fn].apply(el, args);
12662         }
12663         return this;
12664     },
12665     /**
12666      * Returns a flyweight Element of the dom element object at the specified index
12667      * @param {Number} index
12668      * @return {Roo.Element}
12669      */
12670     item : function(index){
12671         if(!this.elements[index]){
12672             return null;
12673         }
12674         this.el.dom = this.elements[index];
12675         return this.el;
12676     },
12677
12678     // fixes scope with flyweight
12679     addListener : function(eventName, handler, scope, opt){
12680         var els = this.elements;
12681         for(var i = 0, len = els.length; i < len; i++) {
12682             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12683         }
12684         return this;
12685     },
12686
12687     /**
12688     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12689     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12690     * a reference to the dom node, use el.dom.</b>
12691     * @param {Function} fn The function to call
12692     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12693     * @return {CompositeElement} this
12694     */
12695     each : function(fn, scope){
12696         var els = this.elements;
12697         var el = this.el;
12698         for(var i = 0, len = els.length; i < len; i++){
12699             el.dom = els[i];
12700                 if(fn.call(scope || el, el, this, i) === false){
12701                 break;
12702             }
12703         }
12704         return this;
12705     },
12706
12707     indexOf : function(el){
12708         return this.elements.indexOf(Roo.getDom(el));
12709     },
12710
12711     replaceElement : function(el, replacement, domReplace){
12712         var index = typeof el == 'number' ? el : this.indexOf(el);
12713         if(index !== -1){
12714             replacement = Roo.getDom(replacement);
12715             if(domReplace){
12716                 var d = this.elements[index];
12717                 d.parentNode.insertBefore(replacement, d);
12718                 d.parentNode.removeChild(d);
12719             }
12720             this.elements.splice(index, 1, replacement);
12721         }
12722         return this;
12723     }
12724 });
12725 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12726
12727 /*
12728  * Based on:
12729  * Ext JS Library 1.1.1
12730  * Copyright(c) 2006-2007, Ext JS, LLC.
12731  *
12732  * Originally Released Under LGPL - original licence link has changed is not relivant.
12733  *
12734  * Fork - LGPL
12735  * <script type="text/javascript">
12736  */
12737
12738  
12739
12740 /**
12741  * @class Roo.data.Connection
12742  * @extends Roo.util.Observable
12743  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12744  * either to a configured URL, or to a URL specified at request time. 
12745  * 
12746  * Requests made by this class are asynchronous, and will return immediately. No data from
12747  * the server will be available to the statement immediately following the {@link #request} call.
12748  * To process returned data, use a callback in the request options object, or an event listener.
12749  * 
12750  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12751  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12752  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12753  * property and, if present, the IFRAME's XML document as the responseXML property.
12754  * 
12755  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12756  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12757  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12758  * standard DOM methods.
12759  * @constructor
12760  * @param {Object} config a configuration object.
12761  */
12762 Roo.data.Connection = function(config){
12763     Roo.apply(this, config);
12764     this.addEvents({
12765         /**
12766          * @event beforerequest
12767          * Fires before a network request is made to retrieve a data object.
12768          * @param {Connection} conn This Connection object.
12769          * @param {Object} options The options config object passed to the {@link #request} method.
12770          */
12771         "beforerequest" : true,
12772         /**
12773          * @event requestcomplete
12774          * Fires if the request was successfully completed.
12775          * @param {Connection} conn This Connection object.
12776          * @param {Object} response The XHR object containing the response data.
12777          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12778          * @param {Object} options The options config object passed to the {@link #request} method.
12779          */
12780         "requestcomplete" : true,
12781         /**
12782          * @event requestexception
12783          * Fires if an error HTTP status was returned from the server.
12784          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12785          * @param {Connection} conn This Connection object.
12786          * @param {Object} response The XHR object containing the response data.
12787          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12788          * @param {Object} options The options config object passed to the {@link #request} method.
12789          */
12790         "requestexception" : true
12791     });
12792     Roo.data.Connection.superclass.constructor.call(this);
12793 };
12794
12795 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12796     /**
12797      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12798      */
12799     /**
12800      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12801      * extra parameters to each request made by this object. (defaults to undefined)
12802      */
12803     /**
12804      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12805      *  to each request made by this object. (defaults to undefined)
12806      */
12807     /**
12808      * @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)
12809      */
12810     /**
12811      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12812      */
12813     timeout : 30000,
12814     /**
12815      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12816      * @type Boolean
12817      */
12818     autoAbort:false,
12819
12820     /**
12821      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12822      * @type Boolean
12823      */
12824     disableCaching: true,
12825
12826     /**
12827      * Sends an HTTP request to a remote server.
12828      * @param {Object} options An object which may contain the following properties:<ul>
12829      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12830      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12831      * request, a url encoded string or a function to call to get either.</li>
12832      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12833      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12834      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12835      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12836      * <li>options {Object} The parameter to the request call.</li>
12837      * <li>success {Boolean} True if the request succeeded.</li>
12838      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12839      * </ul></li>
12840      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12841      * The callback is passed the following parameters:<ul>
12842      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12843      * <li>options {Object} The parameter to the request call.</li>
12844      * </ul></li>
12845      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12846      * The callback is passed the following parameters:<ul>
12847      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12848      * <li>options {Object} The parameter to the request call.</li>
12849      * </ul></li>
12850      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12851      * for the callback function. Defaults to the browser window.</li>
12852      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12853      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12854      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12855      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12856      * params for the post data. Any params will be appended to the URL.</li>
12857      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12858      * </ul>
12859      * @return {Number} transactionId
12860      */
12861     request : function(o){
12862         if(this.fireEvent("beforerequest", this, o) !== false){
12863             var p = o.params;
12864
12865             if(typeof p == "function"){
12866                 p = p.call(o.scope||window, o);
12867             }
12868             if(typeof p == "object"){
12869                 p = Roo.urlEncode(o.params);
12870             }
12871             if(this.extraParams){
12872                 var extras = Roo.urlEncode(this.extraParams);
12873                 p = p ? (p + '&' + extras) : extras;
12874             }
12875
12876             var url = o.url || this.url;
12877             if(typeof url == 'function'){
12878                 url = url.call(o.scope||window, o);
12879             }
12880
12881             if(o.form){
12882                 var form = Roo.getDom(o.form);
12883                 url = url || form.action;
12884
12885                 var enctype = form.getAttribute("enctype");
12886                 
12887                 if (o.formData) {
12888                     return this.doFormDataUpload(o, url);
12889                 }
12890                 
12891                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12892                     return this.doFormUpload(o, p, url);
12893                 }
12894                 var f = Roo.lib.Ajax.serializeForm(form);
12895                 p = p ? (p + '&' + f) : f;
12896             }
12897             
12898             if (!o.form && o.formData) {
12899                 o.formData = o.formData === true ? new FormData() : o.formData;
12900                 for (var k in o.params) {
12901                     o.formData.append(k,o.params[k]);
12902                 }
12903                     
12904                 return this.doFormDataUpload(o, url);
12905             }
12906             
12907
12908             var hs = o.headers;
12909             if(this.defaultHeaders){
12910                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12911                 if(!o.headers){
12912                     o.headers = hs;
12913                 }
12914             }
12915
12916             var cb = {
12917                 success: this.handleResponse,
12918                 failure: this.handleFailure,
12919                 scope: this,
12920                 argument: {options: o},
12921                 timeout : o.timeout || this.timeout
12922             };
12923
12924             var method = o.method||this.method||(p ? "POST" : "GET");
12925
12926             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12927                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12928             }
12929
12930             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12931                 if(o.autoAbort){
12932                     this.abort();
12933                 }
12934             }else if(this.autoAbort !== false){
12935                 this.abort();
12936             }
12937
12938             if((method == 'GET' && p) || o.xmlData){
12939                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12940                 p = '';
12941             }
12942             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12943             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12944             Roo.lib.Ajax.useDefaultHeader == true;
12945             return this.transId;
12946         }else{
12947             Roo.callback(o.callback, o.scope, [o, null, null]);
12948             return null;
12949         }
12950     },
12951
12952     /**
12953      * Determine whether this object has a request outstanding.
12954      * @param {Number} transactionId (Optional) defaults to the last transaction
12955      * @return {Boolean} True if there is an outstanding request.
12956      */
12957     isLoading : function(transId){
12958         if(transId){
12959             return Roo.lib.Ajax.isCallInProgress(transId);
12960         }else{
12961             return this.transId ? true : false;
12962         }
12963     },
12964
12965     /**
12966      * Aborts any outstanding request.
12967      * @param {Number} transactionId (Optional) defaults to the last transaction
12968      */
12969     abort : function(transId){
12970         if(transId || this.isLoading()){
12971             Roo.lib.Ajax.abort(transId || this.transId);
12972         }
12973     },
12974
12975     // private
12976     handleResponse : function(response){
12977         this.transId = false;
12978         var options = response.argument.options;
12979         response.argument = options ? options.argument : null;
12980         this.fireEvent("requestcomplete", this, response, options);
12981         Roo.callback(options.success, options.scope, [response, options]);
12982         Roo.callback(options.callback, options.scope, [options, true, response]);
12983     },
12984
12985     // private
12986     handleFailure : function(response, e){
12987         this.transId = false;
12988         var options = response.argument.options;
12989         response.argument = options ? options.argument : null;
12990         this.fireEvent("requestexception", this, response, options, e);
12991         Roo.callback(options.failure, options.scope, [response, options]);
12992         Roo.callback(options.callback, options.scope, [options, false, response]);
12993     },
12994
12995     // private
12996     doFormUpload : function(o, ps, url){
12997         var id = Roo.id();
12998         var frame = document.createElement('iframe');
12999         frame.id = id;
13000         frame.name = id;
13001         frame.className = 'x-hidden';
13002         if(Roo.isIE){
13003             frame.src = Roo.SSL_SECURE_URL;
13004         }
13005         document.body.appendChild(frame);
13006
13007         if(Roo.isIE){
13008            document.frames[id].name = id;
13009         }
13010
13011         var form = Roo.getDom(o.form);
13012         form.target = id;
13013         form.method = 'POST';
13014         form.enctype = form.encoding = 'multipart/form-data';
13015         if(url){
13016             form.action = url;
13017         }
13018
13019         var hiddens, hd;
13020         if(ps){ // add dynamic params
13021             hiddens = [];
13022             ps = Roo.urlDecode(ps, false);
13023             for(var k in ps){
13024                 if(ps.hasOwnProperty(k)){
13025                     hd = document.createElement('input');
13026                     hd.type = 'hidden';
13027                     hd.name = k;
13028                     hd.value = ps[k];
13029                     form.appendChild(hd);
13030                     hiddens.push(hd);
13031                 }
13032             }
13033         }
13034
13035         function cb(){
13036             var r = {  // bogus response object
13037                 responseText : '',
13038                 responseXML : null
13039             };
13040
13041             r.argument = o ? o.argument : null;
13042
13043             try { //
13044                 var doc;
13045                 if(Roo.isIE){
13046                     doc = frame.contentWindow.document;
13047                 }else {
13048                     doc = (frame.contentDocument || window.frames[id].document);
13049                 }
13050                 if(doc && doc.body){
13051                     r.responseText = doc.body.innerHTML;
13052                 }
13053                 if(doc && doc.XMLDocument){
13054                     r.responseXML = doc.XMLDocument;
13055                 }else {
13056                     r.responseXML = doc;
13057                 }
13058             }
13059             catch(e) {
13060                 // ignore
13061             }
13062
13063             Roo.EventManager.removeListener(frame, 'load', cb, this);
13064
13065             this.fireEvent("requestcomplete", this, r, o);
13066             Roo.callback(o.success, o.scope, [r, o]);
13067             Roo.callback(o.callback, o.scope, [o, true, r]);
13068
13069             setTimeout(function(){document.body.removeChild(frame);}, 100);
13070         }
13071
13072         Roo.EventManager.on(frame, 'load', cb, this);
13073         form.submit();
13074
13075         if(hiddens){ // remove dynamic params
13076             for(var i = 0, len = hiddens.length; i < len; i++){
13077                 form.removeChild(hiddens[i]);
13078             }
13079         }
13080     },
13081     // this is a 'formdata version???'
13082     
13083     
13084     doFormDataUpload : function(o,  url)
13085     {
13086         var formData;
13087         if (o.form) {
13088             var form =  Roo.getDom(o.form);
13089             form.enctype = form.encoding = 'multipart/form-data';
13090             formData = o.formData === true ? new FormData(form) : o.formData;
13091         } else {
13092             formData = o.formData === true ? new FormData() : o.formData;
13093         }
13094         
13095       
13096         var cb = {
13097             success: this.handleResponse,
13098             failure: this.handleFailure,
13099             scope: this,
13100             argument: {options: o},
13101             timeout : o.timeout || this.timeout
13102         };
13103  
13104         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13105             if(o.autoAbort){
13106                 this.abort();
13107             }
13108         }else if(this.autoAbort !== false){
13109             this.abort();
13110         }
13111
13112         //Roo.lib.Ajax.defaultPostHeader = null;
13113         Roo.lib.Ajax.useDefaultHeader = false;
13114         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13115         Roo.lib.Ajax.useDefaultHeader = true;
13116  
13117          
13118     }
13119     
13120 });
13121 /*
13122  * Based on:
13123  * Ext JS Library 1.1.1
13124  * Copyright(c) 2006-2007, Ext JS, LLC.
13125  *
13126  * Originally Released Under LGPL - original licence link has changed is not relivant.
13127  *
13128  * Fork - LGPL
13129  * <script type="text/javascript">
13130  */
13131  
13132 /**
13133  * Global Ajax request class.
13134  * 
13135  * @class Roo.Ajax
13136  * @extends Roo.data.Connection
13137  * @static
13138  * 
13139  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13140  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13141  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13142  * @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)
13143  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13144  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13145  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13146  */
13147 Roo.Ajax = new Roo.data.Connection({
13148     // fix up the docs
13149     /**
13150      * @scope Roo.Ajax
13151      * @type {Boolear} 
13152      */
13153     autoAbort : false,
13154
13155     /**
13156      * Serialize the passed form into a url encoded string
13157      * @scope Roo.Ajax
13158      * @param {String/HTMLElement} form
13159      * @return {String}
13160      */
13161     serializeForm : function(form){
13162         return Roo.lib.Ajax.serializeForm(form);
13163     }
13164 });/*
13165  * Based on:
13166  * Ext JS Library 1.1.1
13167  * Copyright(c) 2006-2007, Ext JS, LLC.
13168  *
13169  * Originally Released Under LGPL - original licence link has changed is not relivant.
13170  *
13171  * Fork - LGPL
13172  * <script type="text/javascript">
13173  */
13174
13175  
13176 /**
13177  * @class Roo.UpdateManager
13178  * @extends Roo.util.Observable
13179  * Provides AJAX-style update for Element object.<br><br>
13180  * Usage:<br>
13181  * <pre><code>
13182  * // Get it from a Roo.Element object
13183  * var el = Roo.get("foo");
13184  * var mgr = el.getUpdateManager();
13185  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13186  * ...
13187  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13188  * <br>
13189  * // or directly (returns the same UpdateManager instance)
13190  * var mgr = new Roo.UpdateManager("myElementId");
13191  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13192  * mgr.on("update", myFcnNeedsToKnow);
13193  * <br>
13194    // short handed call directly from the element object
13195    Roo.get("foo").load({
13196         url: "bar.php",
13197         scripts:true,
13198         params: "for=bar",
13199         text: "Loading Foo..."
13200    });
13201  * </code></pre>
13202  * @constructor
13203  * Create new UpdateManager directly.
13204  * @param {String/HTMLElement/Roo.Element} el The element to update
13205  * @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).
13206  */
13207 Roo.UpdateManager = function(el, forceNew){
13208     el = Roo.get(el);
13209     if(!forceNew && el.updateManager){
13210         return el.updateManager;
13211     }
13212     /**
13213      * The Element object
13214      * @type Roo.Element
13215      */
13216     this.el = el;
13217     /**
13218      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13219      * @type String
13220      */
13221     this.defaultUrl = null;
13222
13223     this.addEvents({
13224         /**
13225          * @event beforeupdate
13226          * Fired before an update is made, return false from your handler and the update is cancelled.
13227          * @param {Roo.Element} el
13228          * @param {String/Object/Function} url
13229          * @param {String/Object} params
13230          */
13231         "beforeupdate": true,
13232         /**
13233          * @event update
13234          * Fired after successful update is made.
13235          * @param {Roo.Element} el
13236          * @param {Object} oResponseObject The response Object
13237          */
13238         "update": true,
13239         /**
13240          * @event failure
13241          * Fired on update failure.
13242          * @param {Roo.Element} el
13243          * @param {Object} oResponseObject The response Object
13244          */
13245         "failure": true
13246     });
13247     var d = Roo.UpdateManager.defaults;
13248     /**
13249      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13250      * @type String
13251      */
13252     this.sslBlankUrl = d.sslBlankUrl;
13253     /**
13254      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13255      * @type Boolean
13256      */
13257     this.disableCaching = d.disableCaching;
13258     /**
13259      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13260      * @type String
13261      */
13262     this.indicatorText = d.indicatorText;
13263     /**
13264      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13265      * @type String
13266      */
13267     this.showLoadIndicator = d.showLoadIndicator;
13268     /**
13269      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13270      * @type Number
13271      */
13272     this.timeout = d.timeout;
13273
13274     /**
13275      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13276      * @type Boolean
13277      */
13278     this.loadScripts = d.loadScripts;
13279
13280     /**
13281      * Transaction object of current executing transaction
13282      */
13283     this.transaction = null;
13284
13285     /**
13286      * @private
13287      */
13288     this.autoRefreshProcId = null;
13289     /**
13290      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13291      * @type Function
13292      */
13293     this.refreshDelegate = this.refresh.createDelegate(this);
13294     /**
13295      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13296      * @type Function
13297      */
13298     this.updateDelegate = this.update.createDelegate(this);
13299     /**
13300      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13301      * @type Function
13302      */
13303     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13304     /**
13305      * @private
13306      */
13307     this.successDelegate = this.processSuccess.createDelegate(this);
13308     /**
13309      * @private
13310      */
13311     this.failureDelegate = this.processFailure.createDelegate(this);
13312
13313     if(!this.renderer){
13314      /**
13315       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13316       */
13317     this.renderer = new Roo.UpdateManager.BasicRenderer();
13318     }
13319     
13320     Roo.UpdateManager.superclass.constructor.call(this);
13321 };
13322
13323 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13324     /**
13325      * Get the Element this UpdateManager is bound to
13326      * @return {Roo.Element} The element
13327      */
13328     getEl : function(){
13329         return this.el;
13330     },
13331     /**
13332      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13333      * @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:
13334 <pre><code>
13335 um.update({<br/>
13336     url: "your-url.php",<br/>
13337     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13338     callback: yourFunction,<br/>
13339     scope: yourObject, //(optional scope)  <br/>
13340     discardUrl: false, <br/>
13341     nocache: false,<br/>
13342     text: "Loading...",<br/>
13343     timeout: 30,<br/>
13344     scripts: false<br/>
13345 });
13346 </code></pre>
13347      * The only required property is url. The optional properties nocache, text and scripts
13348      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13349      * @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}
13350      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13351      * @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.
13352      */
13353     update : function(url, params, callback, discardUrl){
13354         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13355             var method = this.method,
13356                 cfg;
13357             if(typeof url == "object"){ // must be config object
13358                 cfg = url;
13359                 url = cfg.url;
13360                 params = params || cfg.params;
13361                 callback = callback || cfg.callback;
13362                 discardUrl = discardUrl || cfg.discardUrl;
13363                 if(callback && cfg.scope){
13364                     callback = callback.createDelegate(cfg.scope);
13365                 }
13366                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13367                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13368                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13369                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13370                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13371             }
13372             this.showLoading();
13373             if(!discardUrl){
13374                 this.defaultUrl = url;
13375             }
13376             if(typeof url == "function"){
13377                 url = url.call(this);
13378             }
13379
13380             method = method || (params ? "POST" : "GET");
13381             if(method == "GET"){
13382                 url = this.prepareUrl(url);
13383             }
13384
13385             var o = Roo.apply(cfg ||{}, {
13386                 url : url,
13387                 params: params,
13388                 success: this.successDelegate,
13389                 failure: this.failureDelegate,
13390                 callback: undefined,
13391                 timeout: (this.timeout*1000),
13392                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13393             });
13394             Roo.log("updated manager called with timeout of " + o.timeout);
13395             this.transaction = Roo.Ajax.request(o);
13396         }
13397     },
13398
13399     /**
13400      * 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.
13401      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13402      * @param {String/HTMLElement} form The form Id or form element
13403      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13404      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13405      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13406      */
13407     formUpdate : function(form, url, reset, callback){
13408         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13409             if(typeof url == "function"){
13410                 url = url.call(this);
13411             }
13412             form = Roo.getDom(form);
13413             this.transaction = Roo.Ajax.request({
13414                 form: form,
13415                 url:url,
13416                 success: this.successDelegate,
13417                 failure: this.failureDelegate,
13418                 timeout: (this.timeout*1000),
13419                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13420             });
13421             this.showLoading.defer(1, this);
13422         }
13423     },
13424
13425     /**
13426      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13427      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13428      */
13429     refresh : function(callback){
13430         if(this.defaultUrl == null){
13431             return;
13432         }
13433         this.update(this.defaultUrl, null, callback, true);
13434     },
13435
13436     /**
13437      * Set this element to auto refresh.
13438      * @param {Number} interval How often to update (in seconds).
13439      * @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)
13440      * @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}
13441      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13442      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13443      */
13444     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13445         if(refreshNow){
13446             this.update(url || this.defaultUrl, params, callback, true);
13447         }
13448         if(this.autoRefreshProcId){
13449             clearInterval(this.autoRefreshProcId);
13450         }
13451         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13452     },
13453
13454     /**
13455      * Stop auto refresh on this element.
13456      */
13457      stopAutoRefresh : function(){
13458         if(this.autoRefreshProcId){
13459             clearInterval(this.autoRefreshProcId);
13460             delete this.autoRefreshProcId;
13461         }
13462     },
13463
13464     isAutoRefreshing : function(){
13465        return this.autoRefreshProcId ? true : false;
13466     },
13467     /**
13468      * Called to update the element to "Loading" state. Override to perform custom action.
13469      */
13470     showLoading : function(){
13471         if(this.showLoadIndicator){
13472             this.el.update(this.indicatorText);
13473         }
13474     },
13475
13476     /**
13477      * Adds unique parameter to query string if disableCaching = true
13478      * @private
13479      */
13480     prepareUrl : function(url){
13481         if(this.disableCaching){
13482             var append = "_dc=" + (new Date().getTime());
13483             if(url.indexOf("?") !== -1){
13484                 url += "&" + append;
13485             }else{
13486                 url += "?" + append;
13487             }
13488         }
13489         return url;
13490     },
13491
13492     /**
13493      * @private
13494      */
13495     processSuccess : function(response){
13496         this.transaction = null;
13497         if(response.argument.form && response.argument.reset){
13498             try{ // put in try/catch since some older FF releases had problems with this
13499                 response.argument.form.reset();
13500             }catch(e){}
13501         }
13502         if(this.loadScripts){
13503             this.renderer.render(this.el, response, this,
13504                 this.updateComplete.createDelegate(this, [response]));
13505         }else{
13506             this.renderer.render(this.el, response, this);
13507             this.updateComplete(response);
13508         }
13509     },
13510
13511     updateComplete : function(response){
13512         this.fireEvent("update", this.el, response);
13513         if(typeof response.argument.callback == "function"){
13514             response.argument.callback(this.el, true, response);
13515         }
13516     },
13517
13518     /**
13519      * @private
13520      */
13521     processFailure : function(response){
13522         this.transaction = null;
13523         this.fireEvent("failure", this.el, response);
13524         if(typeof response.argument.callback == "function"){
13525             response.argument.callback(this.el, false, response);
13526         }
13527     },
13528
13529     /**
13530      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13531      * @param {Object} renderer The object implementing the render() method
13532      */
13533     setRenderer : function(renderer){
13534         this.renderer = renderer;
13535     },
13536
13537     getRenderer : function(){
13538        return this.renderer;
13539     },
13540
13541     /**
13542      * Set the defaultUrl used for updates
13543      * @param {String/Function} defaultUrl The url or a function to call to get the url
13544      */
13545     setDefaultUrl : function(defaultUrl){
13546         this.defaultUrl = defaultUrl;
13547     },
13548
13549     /**
13550      * Aborts the executing transaction
13551      */
13552     abort : function(){
13553         if(this.transaction){
13554             Roo.Ajax.abort(this.transaction);
13555         }
13556     },
13557
13558     /**
13559      * Returns true if an update is in progress
13560      * @return {Boolean}
13561      */
13562     isUpdating : function(){
13563         if(this.transaction){
13564             return Roo.Ajax.isLoading(this.transaction);
13565         }
13566         return false;
13567     }
13568 });
13569
13570 /**
13571  * @class Roo.UpdateManager.defaults
13572  * @static (not really - but it helps the doc tool)
13573  * The defaults collection enables customizing the default properties of UpdateManager
13574  */
13575    Roo.UpdateManager.defaults = {
13576        /**
13577          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13578          * @type Number
13579          */
13580          timeout : 30,
13581
13582          /**
13583          * True to process scripts by default (Defaults to false).
13584          * @type Boolean
13585          */
13586         loadScripts : false,
13587
13588         /**
13589         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13590         * @type String
13591         */
13592         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13593         /**
13594          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13595          * @type Boolean
13596          */
13597         disableCaching : false,
13598         /**
13599          * Whether to show indicatorText when loading (Defaults to true).
13600          * @type Boolean
13601          */
13602         showLoadIndicator : true,
13603         /**
13604          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13605          * @type String
13606          */
13607         indicatorText : '<div class="loading-indicator">Loading...</div>'
13608    };
13609
13610 /**
13611  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13612  *Usage:
13613  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13614  * @param {String/HTMLElement/Roo.Element} el The element to update
13615  * @param {String} url The url
13616  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13617  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13618  * @static
13619  * @deprecated
13620  * @member Roo.UpdateManager
13621  */
13622 Roo.UpdateManager.updateElement = function(el, url, params, options){
13623     var um = Roo.get(el, true).getUpdateManager();
13624     Roo.apply(um, options);
13625     um.update(url, params, options ? options.callback : null);
13626 };
13627 // alias for backwards compat
13628 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13629 /**
13630  * @class Roo.UpdateManager.BasicRenderer
13631  * Default Content renderer. Updates the elements innerHTML with the responseText.
13632  */
13633 Roo.UpdateManager.BasicRenderer = function(){};
13634
13635 Roo.UpdateManager.BasicRenderer.prototype = {
13636     /**
13637      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13638      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13639      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13640      * @param {Roo.Element} el The element being rendered
13641      * @param {Object} response The YUI Connect response object
13642      * @param {UpdateManager} updateManager The calling update manager
13643      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13644      */
13645      render : function(el, response, updateManager, callback){
13646         el.update(response.responseText, updateManager.loadScripts, callback);
13647     }
13648 };
13649 /*
13650  * Based on:
13651  * Roo JS
13652  * (c)) Alan Knowles
13653  * Licence : LGPL
13654  */
13655
13656
13657 /**
13658  * @class Roo.DomTemplate
13659  * @extends Roo.Template
13660  * An effort at a dom based template engine..
13661  *
13662  * Similar to XTemplate, except it uses dom parsing to create the template..
13663  *
13664  * Supported features:
13665  *
13666  *  Tags:
13667
13668 <pre><code>
13669       {a_variable} - output encoded.
13670       {a_variable.format:("Y-m-d")} - call a method on the variable
13671       {a_variable:raw} - unencoded output
13672       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13673       {a_variable:this.method_on_template(...)} - call a method on the template object.
13674  
13675 </code></pre>
13676  *  The tpl tag:
13677 <pre><code>
13678         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13679         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13680         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13681         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13682   
13683 </code></pre>
13684  *      
13685  */
13686 Roo.DomTemplate = function()
13687 {
13688      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13689      if (this.html) {
13690         this.compile();
13691      }
13692 };
13693
13694
13695 Roo.extend(Roo.DomTemplate, Roo.Template, {
13696     /**
13697      * id counter for sub templates.
13698      */
13699     id : 0,
13700     /**
13701      * flag to indicate if dom parser is inside a pre,
13702      * it will strip whitespace if not.
13703      */
13704     inPre : false,
13705     
13706     /**
13707      * The various sub templates
13708      */
13709     tpls : false,
13710     
13711     
13712     
13713     /**
13714      *
13715      * basic tag replacing syntax
13716      * WORD:WORD()
13717      *
13718      * // you can fake an object call by doing this
13719      *  x.t:(test,tesT) 
13720      * 
13721      */
13722     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13723     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13724     
13725     iterChild : function (node, method) {
13726         
13727         var oldPre = this.inPre;
13728         if (node.tagName == 'PRE') {
13729             this.inPre = true;
13730         }
13731         for( var i = 0; i < node.childNodes.length; i++) {
13732             method.call(this, node.childNodes[i]);
13733         }
13734         this.inPre = oldPre;
13735     },
13736     
13737     
13738     
13739     /**
13740      * compile the template
13741      *
13742      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13743      *
13744      */
13745     compile: function()
13746     {
13747         var s = this.html;
13748         
13749         // covert the html into DOM...
13750         var doc = false;
13751         var div =false;
13752         try {
13753             doc = document.implementation.createHTMLDocument("");
13754             doc.documentElement.innerHTML =   this.html  ;
13755             div = doc.documentElement;
13756         } catch (e) {
13757             // old IE... - nasty -- it causes all sorts of issues.. with
13758             // images getting pulled from server..
13759             div = document.createElement('div');
13760             div.innerHTML = this.html;
13761         }
13762         //doc.documentElement.innerHTML = htmlBody
13763          
13764         
13765         
13766         this.tpls = [];
13767         var _t = this;
13768         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13769         
13770         var tpls = this.tpls;
13771         
13772         // create a top level template from the snippet..
13773         
13774         //Roo.log(div.innerHTML);
13775         
13776         var tpl = {
13777             uid : 'master',
13778             id : this.id++,
13779             attr : false,
13780             value : false,
13781             body : div.innerHTML,
13782             
13783             forCall : false,
13784             execCall : false,
13785             dom : div,
13786             isTop : true
13787             
13788         };
13789         tpls.unshift(tpl);
13790         
13791         
13792         // compile them...
13793         this.tpls = [];
13794         Roo.each(tpls, function(tp){
13795             this.compileTpl(tp);
13796             this.tpls[tp.id] = tp;
13797         }, this);
13798         
13799         this.master = tpls[0];
13800         return this;
13801         
13802         
13803     },
13804     
13805     compileNode : function(node, istop) {
13806         // test for
13807         //Roo.log(node);
13808         
13809         
13810         // skip anything not a tag..
13811         if (node.nodeType != 1) {
13812             if (node.nodeType == 3 && !this.inPre) {
13813                 // reduce white space..
13814                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13815                 
13816             }
13817             return;
13818         }
13819         
13820         var tpl = {
13821             uid : false,
13822             id : false,
13823             attr : false,
13824             value : false,
13825             body : '',
13826             
13827             forCall : false,
13828             execCall : false,
13829             dom : false,
13830             isTop : istop
13831             
13832             
13833         };
13834         
13835         
13836         switch(true) {
13837             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13838             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13839             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13840             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13841             // no default..
13842         }
13843         
13844         
13845         if (!tpl.attr) {
13846             // just itterate children..
13847             this.iterChild(node,this.compileNode);
13848             return;
13849         }
13850         tpl.uid = this.id++;
13851         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13852         node.removeAttribute('roo-'+ tpl.attr);
13853         if (tpl.attr != 'name') {
13854             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13855             node.parentNode.replaceChild(placeholder,  node);
13856         } else {
13857             
13858             var placeholder =  document.createElement('span');
13859             placeholder.className = 'roo-tpl-' + tpl.value;
13860             node.parentNode.replaceChild(placeholder,  node);
13861         }
13862         
13863         // parent now sees '{domtplXXXX}
13864         this.iterChild(node,this.compileNode);
13865         
13866         // we should now have node body...
13867         var div = document.createElement('div');
13868         div.appendChild(node);
13869         tpl.dom = node;
13870         // this has the unfortunate side effect of converting tagged attributes
13871         // eg. href="{...}" into %7C...%7D
13872         // this has been fixed by searching for those combo's although it's a bit hacky..
13873         
13874         
13875         tpl.body = div.innerHTML;
13876         
13877         
13878          
13879         tpl.id = tpl.uid;
13880         switch(tpl.attr) {
13881             case 'for' :
13882                 switch (tpl.value) {
13883                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13884                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13885                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13886                 }
13887                 break;
13888             
13889             case 'exec':
13890                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13891                 break;
13892             
13893             case 'if':     
13894                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13895                 break;
13896             
13897             case 'name':
13898                 tpl.id  = tpl.value; // replace non characters???
13899                 break;
13900             
13901         }
13902         
13903         
13904         this.tpls.push(tpl);
13905         
13906         
13907         
13908     },
13909     
13910     
13911     
13912     
13913     /**
13914      * Compile a segment of the template into a 'sub-template'
13915      *
13916      * 
13917      * 
13918      *
13919      */
13920     compileTpl : function(tpl)
13921     {
13922         var fm = Roo.util.Format;
13923         var useF = this.disableFormats !== true;
13924         
13925         var sep = Roo.isGecko ? "+\n" : ",\n";
13926         
13927         var undef = function(str) {
13928             Roo.debug && Roo.log("Property not found :"  + str);
13929             return '';
13930         };
13931           
13932         //Roo.log(tpl.body);
13933         
13934         
13935         
13936         var fn = function(m, lbrace, name, format, args)
13937         {
13938             //Roo.log("ARGS");
13939             //Roo.log(arguments);
13940             args = args ? args.replace(/\\'/g,"'") : args;
13941             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13942             if (typeof(format) == 'undefined') {
13943                 format =  'htmlEncode'; 
13944             }
13945             if (format == 'raw' ) {
13946                 format = false;
13947             }
13948             
13949             if(name.substr(0, 6) == 'domtpl'){
13950                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13951             }
13952             
13953             // build an array of options to determine if value is undefined..
13954             
13955             // basically get 'xxxx.yyyy' then do
13956             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13957             //    (function () { Roo.log("Property not found"); return ''; })() :
13958             //    ......
13959             
13960             var udef_ar = [];
13961             var lookfor = '';
13962             Roo.each(name.split('.'), function(st) {
13963                 lookfor += (lookfor.length ? '.': '') + st;
13964                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13965             });
13966             
13967             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13968             
13969             
13970             if(format && useF){
13971                 
13972                 args = args ? ',' + args : "";
13973                  
13974                 if(format.substr(0, 5) != "this."){
13975                     format = "fm." + format + '(';
13976                 }else{
13977                     format = 'this.call("'+ format.substr(5) + '", ';
13978                     args = ", values";
13979                 }
13980                 
13981                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13982             }
13983              
13984             if (args && args.length) {
13985                 // called with xxyx.yuu:(test,test)
13986                 // change to ()
13987                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13988             }
13989             // raw.. - :raw modifier..
13990             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13991             
13992         };
13993         var body;
13994         // branched to use + in gecko and [].join() in others
13995         if(Roo.isGecko){
13996             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13997                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13998                     "';};};";
13999         }else{
14000             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
14001             body.push(tpl.body.replace(/(\r\n|\n)/g,
14002                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
14003             body.push("'].join('');};};");
14004             body = body.join('');
14005         }
14006         
14007         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
14008        
14009         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
14010         eval(body);
14011         
14012         return this;
14013     },
14014      
14015     /**
14016      * same as applyTemplate, except it's done to one of the subTemplates
14017      * when using named templates, you can do:
14018      *
14019      * var str = pl.applySubTemplate('your-name', values);
14020      *
14021      * 
14022      * @param {Number} id of the template
14023      * @param {Object} values to apply to template
14024      * @param {Object} parent (normaly the instance of this object)
14025      */
14026     applySubTemplate : function(id, values, parent)
14027     {
14028         
14029         
14030         var t = this.tpls[id];
14031         
14032         
14033         try { 
14034             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14035                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14036                 return '';
14037             }
14038         } catch(e) {
14039             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14040             Roo.log(values);
14041           
14042             return '';
14043         }
14044         try { 
14045             
14046             if(t.execCall && t.execCall.call(this, values, parent)){
14047                 return '';
14048             }
14049         } catch(e) {
14050             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14051             Roo.log(values);
14052             return '';
14053         }
14054         
14055         try {
14056             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14057             parent = t.target ? values : parent;
14058             if(t.forCall && vs instanceof Array){
14059                 var buf = [];
14060                 for(var i = 0, len = vs.length; i < len; i++){
14061                     try {
14062                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14063                     } catch (e) {
14064                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14065                         Roo.log(e.body);
14066                         //Roo.log(t.compiled);
14067                         Roo.log(vs[i]);
14068                     }   
14069                 }
14070                 return buf.join('');
14071             }
14072         } catch (e) {
14073             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14074             Roo.log(values);
14075             return '';
14076         }
14077         try {
14078             return t.compiled.call(this, vs, parent);
14079         } catch (e) {
14080             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14081             Roo.log(e.body);
14082             //Roo.log(t.compiled);
14083             Roo.log(values);
14084             return '';
14085         }
14086     },
14087
14088    
14089
14090     applyTemplate : function(values){
14091         return this.master.compiled.call(this, values, {});
14092         //var s = this.subs;
14093     },
14094
14095     apply : function(){
14096         return this.applyTemplate.apply(this, arguments);
14097     }
14098
14099  });
14100
14101 Roo.DomTemplate.from = function(el){
14102     el = Roo.getDom(el);
14103     return new Roo.Domtemplate(el.value || el.innerHTML);
14104 };/*
14105  * Based on:
14106  * Ext JS Library 1.1.1
14107  * Copyright(c) 2006-2007, Ext JS, LLC.
14108  *
14109  * Originally Released Under LGPL - original licence link has changed is not relivant.
14110  *
14111  * Fork - LGPL
14112  * <script type="text/javascript">
14113  */
14114
14115 /**
14116  * @class Roo.util.DelayedTask
14117  * Provides a convenient method of performing setTimeout where a new
14118  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14119  * You can use this class to buffer
14120  * the keypress events for a certain number of milliseconds, and perform only if they stop
14121  * for that amount of time.
14122  * @constructor The parameters to this constructor serve as defaults and are not required.
14123  * @param {Function} fn (optional) The default function to timeout
14124  * @param {Object} scope (optional) The default scope of that timeout
14125  * @param {Array} args (optional) The default Array of arguments
14126  */
14127 Roo.util.DelayedTask = function(fn, scope, args){
14128     var id = null, d, t;
14129
14130     var call = function(){
14131         var now = new Date().getTime();
14132         if(now - t >= d){
14133             clearInterval(id);
14134             id = null;
14135             fn.apply(scope, args || []);
14136         }
14137     };
14138     /**
14139      * Cancels any pending timeout and queues a new one
14140      * @param {Number} delay The milliseconds to delay
14141      * @param {Function} newFn (optional) Overrides function passed to constructor
14142      * @param {Object} newScope (optional) Overrides scope passed to constructor
14143      * @param {Array} newArgs (optional) Overrides args passed to constructor
14144      */
14145     this.delay = function(delay, newFn, newScope, newArgs){
14146         if(id && delay != d){
14147             this.cancel();
14148         }
14149         d = delay;
14150         t = new Date().getTime();
14151         fn = newFn || fn;
14152         scope = newScope || scope;
14153         args = newArgs || args;
14154         if(!id){
14155             id = setInterval(call, d);
14156         }
14157     };
14158
14159     /**
14160      * Cancel the last queued timeout
14161      */
14162     this.cancel = function(){
14163         if(id){
14164             clearInterval(id);
14165             id = null;
14166         }
14167     };
14168 };/*
14169  * Based on:
14170  * Ext JS Library 1.1.1
14171  * Copyright(c) 2006-2007, Ext JS, LLC.
14172  *
14173  * Originally Released Under LGPL - original licence link has changed is not relivant.
14174  *
14175  * Fork - LGPL
14176  * <script type="text/javascript">
14177  */
14178 /**
14179  * @class Roo.util.TaskRunner
14180  * Manage background tasks - not sure why this is better that setInterval?
14181  * @static
14182  *
14183  */
14184  
14185 Roo.util.TaskRunner = function(interval){
14186     interval = interval || 10;
14187     var tasks = [], removeQueue = [];
14188     var id = 0;
14189     var running = false;
14190
14191     var stopThread = function(){
14192         running = false;
14193         clearInterval(id);
14194         id = 0;
14195     };
14196
14197     var startThread = function(){
14198         if(!running){
14199             running = true;
14200             id = setInterval(runTasks, interval);
14201         }
14202     };
14203
14204     var removeTask = function(task){
14205         removeQueue.push(task);
14206         if(task.onStop){
14207             task.onStop();
14208         }
14209     };
14210
14211     var runTasks = function(){
14212         if(removeQueue.length > 0){
14213             for(var i = 0, len = removeQueue.length; i < len; i++){
14214                 tasks.remove(removeQueue[i]);
14215             }
14216             removeQueue = [];
14217             if(tasks.length < 1){
14218                 stopThread();
14219                 return;
14220             }
14221         }
14222         var now = new Date().getTime();
14223         for(var i = 0, len = tasks.length; i < len; ++i){
14224             var t = tasks[i];
14225             var itime = now - t.taskRunTime;
14226             if(t.interval <= itime){
14227                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14228                 t.taskRunTime = now;
14229                 if(rt === false || t.taskRunCount === t.repeat){
14230                     removeTask(t);
14231                     return;
14232                 }
14233             }
14234             if(t.duration && t.duration <= (now - t.taskStartTime)){
14235                 removeTask(t);
14236             }
14237         }
14238     };
14239
14240     /**
14241      * Queues a new task.
14242      * @param {Object} task
14243      *
14244      * Task property : interval = how frequent to run.
14245      * Task object should implement
14246      * function run()
14247      * Task object may implement
14248      * function onStop()
14249      */
14250     this.start = function(task){
14251         tasks.push(task);
14252         task.taskStartTime = new Date().getTime();
14253         task.taskRunTime = 0;
14254         task.taskRunCount = 0;
14255         startThread();
14256         return task;
14257     };
14258     /**
14259      * Stop  new task.
14260      * @param {Object} task
14261      */
14262     this.stop = function(task){
14263         removeTask(task);
14264         return task;
14265     };
14266     /**
14267      * Stop all Tasks
14268      */
14269     this.stopAll = function(){
14270         stopThread();
14271         for(var i = 0, len = tasks.length; i < len; i++){
14272             if(tasks[i].onStop){
14273                 tasks[i].onStop();
14274             }
14275         }
14276         tasks = [];
14277         removeQueue = [];
14278     };
14279 };
14280
14281 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291
14292  
14293 /**
14294  * @class Roo.util.MixedCollection
14295  * @extends Roo.util.Observable
14296  * A Collection class that maintains both numeric indexes and keys and exposes events.
14297  * @constructor
14298  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14299  * collection (defaults to false)
14300  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14301  * and return the key value for that item.  This is used when available to look up the key on items that
14302  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14303  * equivalent to providing an implementation for the {@link #getKey} method.
14304  */
14305 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14306     this.items = [];
14307     this.map = {};
14308     this.keys = [];
14309     this.length = 0;
14310     this.addEvents({
14311         /**
14312          * @event clear
14313          * Fires when the collection is cleared.
14314          */
14315         "clear" : true,
14316         /**
14317          * @event add
14318          * Fires when an item is added to the collection.
14319          * @param {Number} index The index at which the item was added.
14320          * @param {Object} o The item added.
14321          * @param {String} key The key associated with the added item.
14322          */
14323         "add" : true,
14324         /**
14325          * @event replace
14326          * Fires when an item is replaced in the collection.
14327          * @param {String} key he key associated with the new added.
14328          * @param {Object} old The item being replaced.
14329          * @param {Object} new The new item.
14330          */
14331         "replace" : true,
14332         /**
14333          * @event remove
14334          * Fires when an item is removed from the collection.
14335          * @param {Object} o The item being removed.
14336          * @param {String} key (optional) The key associated with the removed item.
14337          */
14338         "remove" : true,
14339         "sort" : true
14340     });
14341     this.allowFunctions = allowFunctions === true;
14342     if(keyFn){
14343         this.getKey = keyFn;
14344     }
14345     Roo.util.MixedCollection.superclass.constructor.call(this);
14346 };
14347
14348 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14349     allowFunctions : false,
14350     
14351 /**
14352  * Adds an item to the collection.
14353  * @param {String} key The key to associate with the item
14354  * @param {Object} o The item to add.
14355  * @return {Object} The item added.
14356  */
14357     add : function(key, o){
14358         if(arguments.length == 1){
14359             o = arguments[0];
14360             key = this.getKey(o);
14361         }
14362         if(typeof key == "undefined" || key === null){
14363             this.length++;
14364             this.items.push(o);
14365             this.keys.push(null);
14366         }else{
14367             var old = this.map[key];
14368             if(old){
14369                 return this.replace(key, o);
14370             }
14371             this.length++;
14372             this.items.push(o);
14373             this.map[key] = o;
14374             this.keys.push(key);
14375         }
14376         this.fireEvent("add", this.length-1, o, key);
14377         return o;
14378     },
14379        
14380 /**
14381   * MixedCollection has a generic way to fetch keys if you implement getKey.
14382 <pre><code>
14383 // normal way
14384 var mc = new Roo.util.MixedCollection();
14385 mc.add(someEl.dom.id, someEl);
14386 mc.add(otherEl.dom.id, otherEl);
14387 //and so on
14388
14389 // using getKey
14390 var mc = new Roo.util.MixedCollection();
14391 mc.getKey = function(el){
14392    return el.dom.id;
14393 };
14394 mc.add(someEl);
14395 mc.add(otherEl);
14396
14397 // or via the constructor
14398 var mc = new Roo.util.MixedCollection(false, function(el){
14399    return el.dom.id;
14400 });
14401 mc.add(someEl);
14402 mc.add(otherEl);
14403 </code></pre>
14404  * @param o {Object} The item for which to find the key.
14405  * @return {Object} The key for the passed item.
14406  */
14407     getKey : function(o){
14408          return o.id; 
14409     },
14410    
14411 /**
14412  * Replaces an item in the collection.
14413  * @param {String} key The key associated with the item to replace, or the item to replace.
14414  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14415  * @return {Object}  The new item.
14416  */
14417     replace : function(key, o){
14418         if(arguments.length == 1){
14419             o = arguments[0];
14420             key = this.getKey(o);
14421         }
14422         var old = this.item(key);
14423         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14424              return this.add(key, o);
14425         }
14426         var index = this.indexOfKey(key);
14427         this.items[index] = o;
14428         this.map[key] = o;
14429         this.fireEvent("replace", key, old, o);
14430         return o;
14431     },
14432    
14433 /**
14434  * Adds all elements of an Array or an Object to the collection.
14435  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14436  * an Array of values, each of which are added to the collection.
14437  */
14438     addAll : function(objs){
14439         if(arguments.length > 1 || objs instanceof Array){
14440             var args = arguments.length > 1 ? arguments : objs;
14441             for(var i = 0, len = args.length; i < len; i++){
14442                 this.add(args[i]);
14443             }
14444         }else{
14445             for(var key in objs){
14446                 if(this.allowFunctions || typeof objs[key] != "function"){
14447                     this.add(key, objs[key]);
14448                 }
14449             }
14450         }
14451     },
14452    
14453 /**
14454  * Executes the specified function once for every item in the collection, passing each
14455  * item as the first and only parameter. returning false from the function will stop the iteration.
14456  * @param {Function} fn The function to execute for each item.
14457  * @param {Object} scope (optional) The scope in which to execute the function.
14458  */
14459     each : function(fn, scope){
14460         var items = [].concat(this.items); // each safe for removal
14461         for(var i = 0, len = items.length; i < len; i++){
14462             if(fn.call(scope || items[i], items[i], i, len) === false){
14463                 break;
14464             }
14465         }
14466     },
14467    
14468 /**
14469  * Executes the specified function once for every key in the collection, passing each
14470  * key, and its associated item as the first two parameters.
14471  * @param {Function} fn The function to execute for each item.
14472  * @param {Object} scope (optional) The scope in which to execute the function.
14473  */
14474     eachKey : function(fn, scope){
14475         for(var i = 0, len = this.keys.length; i < len; i++){
14476             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14477         }
14478     },
14479    
14480 /**
14481  * Returns the first item in the collection which elicits a true return value from the
14482  * passed selection function.
14483  * @param {Function} fn The selection function to execute for each item.
14484  * @param {Object} scope (optional) The scope in which to execute the function.
14485  * @return {Object} The first item in the collection which returned true from the selection function.
14486  */
14487     find : function(fn, scope){
14488         for(var i = 0, len = this.items.length; i < len; i++){
14489             if(fn.call(scope || window, this.items[i], this.keys[i])){
14490                 return this.items[i];
14491             }
14492         }
14493         return null;
14494     },
14495    
14496 /**
14497  * Inserts an item at the specified index in the collection.
14498  * @param {Number} index The index to insert the item at.
14499  * @param {String} key The key to associate with the new item, or the item itself.
14500  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14501  * @return {Object} The item inserted.
14502  */
14503     insert : function(index, key, o){
14504         if(arguments.length == 2){
14505             o = arguments[1];
14506             key = this.getKey(o);
14507         }
14508         if(index >= this.length){
14509             return this.add(key, o);
14510         }
14511         this.length++;
14512         this.items.splice(index, 0, o);
14513         if(typeof key != "undefined" && key != null){
14514             this.map[key] = o;
14515         }
14516         this.keys.splice(index, 0, key);
14517         this.fireEvent("add", index, o, key);
14518         return o;
14519     },
14520    
14521 /**
14522  * Removed an item from the collection.
14523  * @param {Object} o The item to remove.
14524  * @return {Object} The item removed.
14525  */
14526     remove : function(o){
14527         return this.removeAt(this.indexOf(o));
14528     },
14529    
14530 /**
14531  * Remove an item from a specified index in the collection.
14532  * @param {Number} index The index within the collection of the item to remove.
14533  */
14534     removeAt : function(index){
14535         if(index < this.length && index >= 0){
14536             this.length--;
14537             var o = this.items[index];
14538             this.items.splice(index, 1);
14539             var key = this.keys[index];
14540             if(typeof key != "undefined"){
14541                 delete this.map[key];
14542             }
14543             this.keys.splice(index, 1);
14544             this.fireEvent("remove", o, key);
14545         }
14546     },
14547    
14548 /**
14549  * Removed an item associated with the passed key fom the collection.
14550  * @param {String} key The key of the item to remove.
14551  */
14552     removeKey : function(key){
14553         return this.removeAt(this.indexOfKey(key));
14554     },
14555    
14556 /**
14557  * Returns the number of items in the collection.
14558  * @return {Number} the number of items in the collection.
14559  */
14560     getCount : function(){
14561         return this.length; 
14562     },
14563    
14564 /**
14565  * Returns index within the collection of the passed Object.
14566  * @param {Object} o The item to find the index of.
14567  * @return {Number} index of the item.
14568  */
14569     indexOf : function(o){
14570         if(!this.items.indexOf){
14571             for(var i = 0, len = this.items.length; i < len; i++){
14572                 if(this.items[i] == o) {
14573                     return i;
14574                 }
14575             }
14576             return -1;
14577         }else{
14578             return this.items.indexOf(o);
14579         }
14580     },
14581    
14582 /**
14583  * Returns index within the collection of the passed key.
14584  * @param {String} key The key to find the index of.
14585  * @return {Number} index of the key.
14586  */
14587     indexOfKey : function(key){
14588         if(!this.keys.indexOf){
14589             for(var i = 0, len = this.keys.length; i < len; i++){
14590                 if(this.keys[i] == key) {
14591                     return i;
14592                 }
14593             }
14594             return -1;
14595         }else{
14596             return this.keys.indexOf(key);
14597         }
14598     },
14599    
14600 /**
14601  * Returns the item associated with the passed key OR index. Key has priority over index.
14602  * @param {String/Number} key The key or index of the item.
14603  * @return {Object} The item associated with the passed key.
14604  */
14605     item : function(key){
14606         if (key === 'length') {
14607             return null;
14608         }
14609         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14610         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14611     },
14612     
14613 /**
14614  * Returns the item at the specified index.
14615  * @param {Number} index The index of the item.
14616  * @return {Object}
14617  */
14618     itemAt : function(index){
14619         return this.items[index];
14620     },
14621     
14622 /**
14623  * Returns the item associated with the passed key.
14624  * @param {String/Number} key The key of the item.
14625  * @return {Object} The item associated with the passed key.
14626  */
14627     key : function(key){
14628         return this.map[key];
14629     },
14630    
14631 /**
14632  * Returns true if the collection contains the passed Object as an item.
14633  * @param {Object} o  The Object to look for in the collection.
14634  * @return {Boolean} True if the collection contains the Object as an item.
14635  */
14636     contains : function(o){
14637         return this.indexOf(o) != -1;
14638     },
14639    
14640 /**
14641  * Returns true if the collection contains the passed Object as a key.
14642  * @param {String} key The key to look for in the collection.
14643  * @return {Boolean} True if the collection contains the Object as a key.
14644  */
14645     containsKey : function(key){
14646         return typeof this.map[key] != "undefined";
14647     },
14648    
14649 /**
14650  * Removes all items from the collection.
14651  */
14652     clear : function(){
14653         this.length = 0;
14654         this.items = [];
14655         this.keys = [];
14656         this.map = {};
14657         this.fireEvent("clear");
14658     },
14659    
14660 /**
14661  * Returns the first item in the collection.
14662  * @return {Object} the first item in the collection..
14663  */
14664     first : function(){
14665         return this.items[0]; 
14666     },
14667    
14668 /**
14669  * Returns the last item in the collection.
14670  * @return {Object} the last item in the collection..
14671  */
14672     last : function(){
14673         return this.items[this.length-1];   
14674     },
14675     
14676     _sort : function(property, dir, fn){
14677         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14678         fn = fn || function(a, b){
14679             return a-b;
14680         };
14681         var c = [], k = this.keys, items = this.items;
14682         for(var i = 0, len = items.length; i < len; i++){
14683             c[c.length] = {key: k[i], value: items[i], index: i};
14684         }
14685         c.sort(function(a, b){
14686             var v = fn(a[property], b[property]) * dsc;
14687             if(v == 0){
14688                 v = (a.index < b.index ? -1 : 1);
14689             }
14690             return v;
14691         });
14692         for(var i = 0, len = c.length; i < len; i++){
14693             items[i] = c[i].value;
14694             k[i] = c[i].key;
14695         }
14696         this.fireEvent("sort", this);
14697     },
14698     
14699     /**
14700      * Sorts this collection with the passed comparison function
14701      * @param {String} direction (optional) "ASC" or "DESC"
14702      * @param {Function} fn (optional) comparison function
14703      */
14704     sort : function(dir, fn){
14705         this._sort("value", dir, fn);
14706     },
14707     
14708     /**
14709      * Sorts this collection by keys
14710      * @param {String} direction (optional) "ASC" or "DESC"
14711      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14712      */
14713     keySort : function(dir, fn){
14714         this._sort("key", dir, fn || function(a, b){
14715             return String(a).toUpperCase()-String(b).toUpperCase();
14716         });
14717     },
14718     
14719     /**
14720      * Returns a range of items in this collection
14721      * @param {Number} startIndex (optional) defaults to 0
14722      * @param {Number} endIndex (optional) default to the last item
14723      * @return {Array} An array of items
14724      */
14725     getRange : function(start, end){
14726         var items = this.items;
14727         if(items.length < 1){
14728             return [];
14729         }
14730         start = start || 0;
14731         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14732         var r = [];
14733         if(start <= end){
14734             for(var i = start; i <= end; i++) {
14735                     r[r.length] = items[i];
14736             }
14737         }else{
14738             for(var i = start; i >= end; i--) {
14739                     r[r.length] = items[i];
14740             }
14741         }
14742         return r;
14743     },
14744         
14745     /**
14746      * Filter the <i>objects</i> in this collection by a specific property. 
14747      * Returns a new collection that has been filtered.
14748      * @param {String} property A property on your objects
14749      * @param {String/RegExp} value Either string that the property values 
14750      * should start with or a RegExp to test against the property
14751      * @return {MixedCollection} The new filtered collection
14752      */
14753     filter : function(property, value){
14754         if(!value.exec){ // not a regex
14755             value = String(value);
14756             if(value.length == 0){
14757                 return this.clone();
14758             }
14759             value = new RegExp("^" + Roo.escapeRe(value), "i");
14760         }
14761         return this.filterBy(function(o){
14762             return o && value.test(o[property]);
14763         });
14764         },
14765     
14766     /**
14767      * Filter by a function. * Returns a new collection that has been filtered.
14768      * The passed function will be called with each 
14769      * object in the collection. If the function returns true, the value is included 
14770      * otherwise it is filtered.
14771      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14772      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14773      * @return {MixedCollection} The new filtered collection
14774      */
14775     filterBy : function(fn, scope){
14776         var r = new Roo.util.MixedCollection();
14777         r.getKey = this.getKey;
14778         var k = this.keys, it = this.items;
14779         for(var i = 0, len = it.length; i < len; i++){
14780             if(fn.call(scope||this, it[i], k[i])){
14781                                 r.add(k[i], it[i]);
14782                         }
14783         }
14784         return r;
14785     },
14786     
14787     /**
14788      * Creates a duplicate of this collection
14789      * @return {MixedCollection}
14790      */
14791     clone : function(){
14792         var r = new Roo.util.MixedCollection();
14793         var k = this.keys, it = this.items;
14794         for(var i = 0, len = it.length; i < len; i++){
14795             r.add(k[i], it[i]);
14796         }
14797         r.getKey = this.getKey;
14798         return r;
14799     }
14800 });
14801 /**
14802  * Returns the item associated with the passed key or index.
14803  * @method
14804  * @param {String/Number} key The key or index of the item.
14805  * @return {Object} The item associated with the passed key.
14806  */
14807 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14808  * Based on:
14809  * Ext JS Library 1.1.1
14810  * Copyright(c) 2006-2007, Ext JS, LLC.
14811  *
14812  * Originally Released Under LGPL - original licence link has changed is not relivant.
14813  *
14814  * Fork - LGPL
14815  * <script type="text/javascript">
14816  */
14817 /**
14818  * @class Roo.util.JSON
14819  * Modified version of Douglas Crockford"s json.js that doesn"t
14820  * mess with the Object prototype 
14821  * http://www.json.org/js.html
14822  * @static
14823  */
14824 Roo.util.JSON = new (function(){
14825     var useHasOwn = {}.hasOwnProperty ? true : false;
14826     
14827     // crashes Safari in some instances
14828     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14829     
14830     var pad = function(n) {
14831         return n < 10 ? "0" + n : n;
14832     };
14833     
14834     var m = {
14835         "\b": '\\b',
14836         "\t": '\\t',
14837         "\n": '\\n',
14838         "\f": '\\f',
14839         "\r": '\\r',
14840         '"' : '\\"',
14841         "\\": '\\\\'
14842     };
14843
14844     var encodeString = function(s){
14845         if (/["\\\x00-\x1f]/.test(s)) {
14846             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14847                 var c = m[b];
14848                 if(c){
14849                     return c;
14850                 }
14851                 c = b.charCodeAt();
14852                 return "\\u00" +
14853                     Math.floor(c / 16).toString(16) +
14854                     (c % 16).toString(16);
14855             }) + '"';
14856         }
14857         return '"' + s + '"';
14858     };
14859     
14860     var encodeArray = function(o){
14861         var a = ["["], b, i, l = o.length, v;
14862             for (i = 0; i < l; i += 1) {
14863                 v = o[i];
14864                 switch (typeof v) {
14865                     case "undefined":
14866                     case "function":
14867                     case "unknown":
14868                         break;
14869                     default:
14870                         if (b) {
14871                             a.push(',');
14872                         }
14873                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14874                         b = true;
14875                 }
14876             }
14877             a.push("]");
14878             return a.join("");
14879     };
14880     
14881     var encodeDate = function(o){
14882         return '"' + o.getFullYear() + "-" +
14883                 pad(o.getMonth() + 1) + "-" +
14884                 pad(o.getDate()) + "T" +
14885                 pad(o.getHours()) + ":" +
14886                 pad(o.getMinutes()) + ":" +
14887                 pad(o.getSeconds()) + '"';
14888     };
14889     
14890     /**
14891      * Encodes an Object, Array or other value
14892      * @param {Mixed} o The variable to encode
14893      * @return {String} The JSON string
14894      */
14895     this.encode = function(o)
14896     {
14897         // should this be extended to fully wrap stringify..
14898         
14899         if(typeof o == "undefined" || o === null){
14900             return "null";
14901         }else if(o instanceof Array){
14902             return encodeArray(o);
14903         }else if(o instanceof Date){
14904             return encodeDate(o);
14905         }else if(typeof o == "string"){
14906             return encodeString(o);
14907         }else if(typeof o == "number"){
14908             return isFinite(o) ? String(o) : "null";
14909         }else if(typeof o == "boolean"){
14910             return String(o);
14911         }else {
14912             var a = ["{"], b, i, v;
14913             for (i in o) {
14914                 if(!useHasOwn || o.hasOwnProperty(i)) {
14915                     v = o[i];
14916                     switch (typeof v) {
14917                     case "undefined":
14918                     case "function":
14919                     case "unknown":
14920                         break;
14921                     default:
14922                         if(b){
14923                             a.push(',');
14924                         }
14925                         a.push(this.encode(i), ":",
14926                                 v === null ? "null" : this.encode(v));
14927                         b = true;
14928                     }
14929                 }
14930             }
14931             a.push("}");
14932             return a.join("");
14933         }
14934     };
14935     
14936     /**
14937      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14938      * @param {String} json The JSON string
14939      * @return {Object} The resulting object
14940      */
14941     this.decode = function(json){
14942         
14943         return  /** eval:var:json */ eval("(" + json + ')');
14944     };
14945 })();
14946 /** 
14947  * Shorthand for {@link Roo.util.JSON#encode}
14948  * @member Roo encode 
14949  * @method */
14950 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14951 /** 
14952  * Shorthand for {@link Roo.util.JSON#decode}
14953  * @member Roo decode 
14954  * @method */
14955 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14956 /*
14957  * Based on:
14958  * Ext JS Library 1.1.1
14959  * Copyright(c) 2006-2007, Ext JS, LLC.
14960  *
14961  * Originally Released Under LGPL - original licence link has changed is not relivant.
14962  *
14963  * Fork - LGPL
14964  * <script type="text/javascript">
14965  */
14966  
14967 /**
14968  * @class Roo.util.Format
14969  * Reusable data formatting functions
14970  * @static
14971  */
14972 Roo.util.Format = function(){
14973     var trimRe = /^\s+|\s+$/g;
14974     return {
14975         /**
14976          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14977          * @param {String} value The string to truncate
14978          * @param {Number} length The maximum length to allow before truncating
14979          * @return {String} The converted text
14980          */
14981         ellipsis : function(value, len){
14982             if(value && value.length > len){
14983                 return value.substr(0, len-3)+"...";
14984             }
14985             return value;
14986         },
14987
14988         /**
14989          * Checks a reference and converts it to empty string if it is undefined
14990          * @param {Mixed} value Reference to check
14991          * @return {Mixed} Empty string if converted, otherwise the original value
14992          */
14993         undef : function(value){
14994             return typeof value != "undefined" ? value : "";
14995         },
14996
14997         /**
14998          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14999          * @param {String} value The string to encode
15000          * @return {String} The encoded text
15001          */
15002         htmlEncode : function(value){
15003             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
15004         },
15005
15006         /**
15007          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
15008          * @param {String} value The string to decode
15009          * @return {String} The decoded text
15010          */
15011         htmlDecode : function(value){
15012             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
15013         },
15014
15015         /**
15016          * Trims any whitespace from either side of a string
15017          * @param {String} value The text to trim
15018          * @return {String} The trimmed text
15019          */
15020         trim : function(value){
15021             return String(value).replace(trimRe, "");
15022         },
15023
15024         /**
15025          * Returns a substring from within an original string
15026          * @param {String} value The original text
15027          * @param {Number} start The start index of the substring
15028          * @param {Number} length The length of the substring
15029          * @return {String} The substring
15030          */
15031         substr : function(value, start, length){
15032             return String(value).substr(start, length);
15033         },
15034
15035         /**
15036          * Converts a string to all lower case letters
15037          * @param {String} value The text to convert
15038          * @return {String} The converted text
15039          */
15040         lowercase : function(value){
15041             return String(value).toLowerCase();
15042         },
15043
15044         /**
15045          * Converts a string to all upper case letters
15046          * @param {String} value The text to convert
15047          * @return {String} The converted text
15048          */
15049         uppercase : function(value){
15050             return String(value).toUpperCase();
15051         },
15052
15053         /**
15054          * Converts the first character only of a string to upper case
15055          * @param {String} value The text to convert
15056          * @return {String} The converted text
15057          */
15058         capitalize : function(value){
15059             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15060         },
15061
15062         // private
15063         call : function(value, fn){
15064             if(arguments.length > 2){
15065                 var args = Array.prototype.slice.call(arguments, 2);
15066                 args.unshift(value);
15067                  
15068                 return /** eval:var:value */  eval(fn).apply(window, args);
15069             }else{
15070                 /** eval:var:value */
15071                 return /** eval:var:value */ eval(fn).call(window, value);
15072             }
15073         },
15074
15075        
15076         /**
15077          * safer version of Math.toFixed..??/
15078          * @param {Number/String} value The numeric value to format
15079          * @param {Number/String} value Decimal places 
15080          * @return {String} The formatted currency string
15081          */
15082         toFixed : function(v, n)
15083         {
15084             // why not use to fixed - precision is buggered???
15085             if (!n) {
15086                 return Math.round(v-0);
15087             }
15088             var fact = Math.pow(10,n+1);
15089             v = (Math.round((v-0)*fact))/fact;
15090             var z = (''+fact).substring(2);
15091             if (v == Math.floor(v)) {
15092                 return Math.floor(v) + '.' + z;
15093             }
15094             
15095             // now just padd decimals..
15096             var ps = String(v).split('.');
15097             var fd = (ps[1] + z);
15098             var r = fd.substring(0,n); 
15099             var rm = fd.substring(n); 
15100             if (rm < 5) {
15101                 return ps[0] + '.' + r;
15102             }
15103             r*=1; // turn it into a number;
15104             r++;
15105             if (String(r).length != n) {
15106                 ps[0]*=1;
15107                 ps[0]++;
15108                 r = String(r).substring(1); // chop the end off.
15109             }
15110             
15111             return ps[0] + '.' + r;
15112              
15113         },
15114         
15115         /**
15116          * Format a number as US currency
15117          * @param {Number/String} value The numeric value to format
15118          * @return {String} The formatted currency string
15119          */
15120         usMoney : function(v){
15121             return '$' + Roo.util.Format.number(v);
15122         },
15123         
15124         /**
15125          * Format a number
15126          * eventually this should probably emulate php's number_format
15127          * @param {Number/String} value The numeric value to format
15128          * @param {Number} decimals number of decimal places
15129          * @param {String} delimiter for thousands (default comma)
15130          * @return {String} The formatted currency string
15131          */
15132         number : function(v, decimals, thousandsDelimiter)
15133         {
15134             // multiply and round.
15135             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15136             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15137             
15138             var mul = Math.pow(10, decimals);
15139             var zero = String(mul).substring(1);
15140             v = (Math.round((v-0)*mul))/mul;
15141             
15142             // if it's '0' number.. then
15143             
15144             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15145             v = String(v);
15146             var ps = v.split('.');
15147             var whole = ps[0];
15148             
15149             var r = /(\d+)(\d{3})/;
15150             // add comma's
15151             
15152             if(thousandsDelimiter.length != 0) {
15153                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15154             } 
15155             
15156             var sub = ps[1] ?
15157                     // has decimals..
15158                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15159                     // does not have decimals
15160                     (decimals ? ('.' + zero) : '');
15161             
15162             
15163             return whole + sub ;
15164         },
15165         
15166         /**
15167          * Parse a value into a formatted date using the specified format pattern.
15168          * @param {Mixed} value The value to format
15169          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15170          * @return {String} The formatted date string
15171          */
15172         date : function(v, format){
15173             if(!v){
15174                 return "";
15175             }
15176             if(!(v instanceof Date)){
15177                 v = new Date(Date.parse(v));
15178             }
15179             return v.dateFormat(format || Roo.util.Format.defaults.date);
15180         },
15181
15182         /**
15183          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15184          * @param {String} format Any valid date format string
15185          * @return {Function} The date formatting function
15186          */
15187         dateRenderer : function(format){
15188             return function(v){
15189                 return Roo.util.Format.date(v, format);  
15190             };
15191         },
15192
15193         // private
15194         stripTagsRE : /<\/?[^>]+>/gi,
15195         
15196         /**
15197          * Strips all HTML tags
15198          * @param {Mixed} value The text from which to strip tags
15199          * @return {String} The stripped text
15200          */
15201         stripTags : function(v){
15202             return !v ? v : String(v).replace(this.stripTagsRE, "");
15203         },
15204         
15205         /**
15206          * Size in Mb,Gb etc.
15207          * @param {Number} value The number to be formated
15208          * @param {number} decimals how many decimal places
15209          * @return {String} the formated string
15210          */
15211         size : function(value, decimals)
15212         {
15213             var sizes = ['b', 'k', 'M', 'G', 'T'];
15214             if (value == 0) {
15215                 return 0;
15216             }
15217             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15218             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15219         }
15220         
15221         
15222         
15223     };
15224 }();
15225 Roo.util.Format.defaults = {
15226     date : 'd/M/Y'
15227 };/*
15228  * Based on:
15229  * Ext JS Library 1.1.1
15230  * Copyright(c) 2006-2007, Ext JS, LLC.
15231  *
15232  * Originally Released Under LGPL - original licence link has changed is not relivant.
15233  *
15234  * Fork - LGPL
15235  * <script type="text/javascript">
15236  */
15237
15238
15239  
15240
15241 /**
15242  * @class Roo.MasterTemplate
15243  * @extends Roo.Template
15244  * Provides a template that can have child templates. The syntax is:
15245 <pre><code>
15246 var t = new Roo.MasterTemplate(
15247         '&lt;select name="{name}"&gt;',
15248                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15249         '&lt;/select&gt;'
15250 );
15251 t.add('options', {value: 'foo', text: 'bar'});
15252 // or you can add multiple child elements in one shot
15253 t.addAll('options', [
15254     {value: 'foo', text: 'bar'},
15255     {value: 'foo2', text: 'bar2'},
15256     {value: 'foo3', text: 'bar3'}
15257 ]);
15258 // then append, applying the master template values
15259 t.append('my-form', {name: 'my-select'});
15260 </code></pre>
15261 * A name attribute for the child template is not required if you have only one child
15262 * template or you want to refer to them by index.
15263  */
15264 Roo.MasterTemplate = function(){
15265     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15266     this.originalHtml = this.html;
15267     var st = {};
15268     var m, re = this.subTemplateRe;
15269     re.lastIndex = 0;
15270     var subIndex = 0;
15271     while(m = re.exec(this.html)){
15272         var name = m[1], content = m[2];
15273         st[subIndex] = {
15274             name: name,
15275             index: subIndex,
15276             buffer: [],
15277             tpl : new Roo.Template(content)
15278         };
15279         if(name){
15280             st[name] = st[subIndex];
15281         }
15282         st[subIndex].tpl.compile();
15283         st[subIndex].tpl.call = this.call.createDelegate(this);
15284         subIndex++;
15285     }
15286     this.subCount = subIndex;
15287     this.subs = st;
15288 };
15289 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15290     /**
15291     * The regular expression used to match sub templates
15292     * @type RegExp
15293     * @property
15294     */
15295     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15296
15297     /**
15298      * Applies the passed values to a child template.
15299      * @param {String/Number} name (optional) The name or index of the child template
15300      * @param {Array/Object} values The values to be applied to the template
15301      * @return {MasterTemplate} this
15302      */
15303      add : function(name, values){
15304         if(arguments.length == 1){
15305             values = arguments[0];
15306             name = 0;
15307         }
15308         var s = this.subs[name];
15309         s.buffer[s.buffer.length] = s.tpl.apply(values);
15310         return this;
15311     },
15312
15313     /**
15314      * Applies all the passed values to a child template.
15315      * @param {String/Number} name (optional) The name or index of the child template
15316      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15317      * @param {Boolean} reset (optional) True to reset the template first
15318      * @return {MasterTemplate} this
15319      */
15320     fill : function(name, values, reset){
15321         var a = arguments;
15322         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15323             values = a[0];
15324             name = 0;
15325             reset = a[1];
15326         }
15327         if(reset){
15328             this.reset();
15329         }
15330         for(var i = 0, len = values.length; i < len; i++){
15331             this.add(name, values[i]);
15332         }
15333         return this;
15334     },
15335
15336     /**
15337      * Resets the template for reuse
15338      * @return {MasterTemplate} this
15339      */
15340      reset : function(){
15341         var s = this.subs;
15342         for(var i = 0; i < this.subCount; i++){
15343             s[i].buffer = [];
15344         }
15345         return this;
15346     },
15347
15348     applyTemplate : function(values){
15349         var s = this.subs;
15350         var replaceIndex = -1;
15351         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15352             return s[++replaceIndex].buffer.join("");
15353         });
15354         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15355     },
15356
15357     apply : function(){
15358         return this.applyTemplate.apply(this, arguments);
15359     },
15360
15361     compile : function(){return this;}
15362 });
15363
15364 /**
15365  * Alias for fill().
15366  * @method
15367  */
15368 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15369  /**
15370  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15371  * var tpl = Roo.MasterTemplate.from('element-id');
15372  * @param {String/HTMLElement} el
15373  * @param {Object} config
15374  * @static
15375  */
15376 Roo.MasterTemplate.from = function(el, config){
15377     el = Roo.getDom(el);
15378     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15379 };/*
15380  * Based on:
15381  * Ext JS Library 1.1.1
15382  * Copyright(c) 2006-2007, Ext JS, LLC.
15383  *
15384  * Originally Released Under LGPL - original licence link has changed is not relivant.
15385  *
15386  * Fork - LGPL
15387  * <script type="text/javascript">
15388  */
15389
15390  
15391 /**
15392  * @class Roo.util.CSS
15393  * Utility class for manipulating CSS rules
15394  * @static
15395
15396  */
15397 Roo.util.CSS = function(){
15398         var rules = null;
15399         var doc = document;
15400
15401     var camelRe = /(-[a-z])/gi;
15402     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15403
15404    return {
15405    /**
15406     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15407     * tag and appended to the HEAD of the document.
15408     * @param {String|Object} cssText The text containing the css rules
15409     * @param {String} id An id to add to the stylesheet for later removal
15410     * @return {StyleSheet}
15411     */
15412     createStyleSheet : function(cssText, id){
15413         var ss;
15414         var head = doc.getElementsByTagName("head")[0];
15415         var nrules = doc.createElement("style");
15416         nrules.setAttribute("type", "text/css");
15417         if(id){
15418             nrules.setAttribute("id", id);
15419         }
15420         if (typeof(cssText) != 'string') {
15421             // support object maps..
15422             // not sure if this a good idea.. 
15423             // perhaps it should be merged with the general css handling
15424             // and handle js style props.
15425             var cssTextNew = [];
15426             for(var n in cssText) {
15427                 var citems = [];
15428                 for(var k in cssText[n]) {
15429                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15430                 }
15431                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15432                 
15433             }
15434             cssText = cssTextNew.join("\n");
15435             
15436         }
15437        
15438        
15439        if(Roo.isIE){
15440            head.appendChild(nrules);
15441            ss = nrules.styleSheet;
15442            ss.cssText = cssText;
15443        }else{
15444            try{
15445                 nrules.appendChild(doc.createTextNode(cssText));
15446            }catch(e){
15447                nrules.cssText = cssText; 
15448            }
15449            head.appendChild(nrules);
15450            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15451        }
15452        this.cacheStyleSheet(ss);
15453        return ss;
15454    },
15455
15456    /**
15457     * Removes a style or link tag by id
15458     * @param {String} id The id of the tag
15459     */
15460    removeStyleSheet : function(id){
15461        var existing = doc.getElementById(id);
15462        if(existing){
15463            existing.parentNode.removeChild(existing);
15464        }
15465    },
15466
15467    /**
15468     * Dynamically swaps an existing stylesheet reference for a new one
15469     * @param {String} id The id of an existing link tag to remove
15470     * @param {String} url The href of the new stylesheet to include
15471     */
15472    swapStyleSheet : function(id, url){
15473        this.removeStyleSheet(id);
15474        var ss = doc.createElement("link");
15475        ss.setAttribute("rel", "stylesheet");
15476        ss.setAttribute("type", "text/css");
15477        ss.setAttribute("id", id);
15478        ss.setAttribute("href", url);
15479        doc.getElementsByTagName("head")[0].appendChild(ss);
15480    },
15481    
15482    /**
15483     * Refresh the rule cache if you have dynamically added stylesheets
15484     * @return {Object} An object (hash) of rules indexed by selector
15485     */
15486    refreshCache : function(){
15487        return this.getRules(true);
15488    },
15489
15490    // private
15491    cacheStyleSheet : function(stylesheet){
15492        if(!rules){
15493            rules = {};
15494        }
15495        try{// try catch for cross domain access issue
15496            var ssRules = stylesheet.cssRules || stylesheet.rules;
15497            for(var j = ssRules.length-1; j >= 0; --j){
15498                rules[ssRules[j].selectorText] = ssRules[j];
15499            }
15500        }catch(e){}
15501    },
15502    
15503    /**
15504     * Gets all css rules for the document
15505     * @param {Boolean} refreshCache true to refresh the internal cache
15506     * @return {Object} An object (hash) of rules indexed by selector
15507     */
15508    getRules : function(refreshCache){
15509                 if(rules == null || refreshCache){
15510                         rules = {};
15511                         var ds = doc.styleSheets;
15512                         for(var i =0, len = ds.length; i < len; i++){
15513                             try{
15514                         this.cacheStyleSheet(ds[i]);
15515                     }catch(e){} 
15516                 }
15517                 }
15518                 return rules;
15519         },
15520         
15521         /**
15522     * Gets an an individual CSS rule by selector(s)
15523     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15524     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15525     * @return {CSSRule} The CSS rule or null if one is not found
15526     */
15527    getRule : function(selector, refreshCache){
15528                 var rs = this.getRules(refreshCache);
15529                 if(!(selector instanceof Array)){
15530                     return rs[selector];
15531                 }
15532                 for(var i = 0; i < selector.length; i++){
15533                         if(rs[selector[i]]){
15534                                 return rs[selector[i]];
15535                         }
15536                 }
15537                 return null;
15538         },
15539         
15540         
15541         /**
15542     * Updates a rule property
15543     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15544     * @param {String} property The css property
15545     * @param {String} value The new value for the property
15546     * @return {Boolean} true If a rule was found and updated
15547     */
15548    updateRule : function(selector, property, value){
15549                 if(!(selector instanceof Array)){
15550                         var rule = this.getRule(selector);
15551                         if(rule){
15552                                 rule.style[property.replace(camelRe, camelFn)] = value;
15553                                 return true;
15554                         }
15555                 }else{
15556                         for(var i = 0; i < selector.length; i++){
15557                                 if(this.updateRule(selector[i], property, value)){
15558                                         return true;
15559                                 }
15560                         }
15561                 }
15562                 return false;
15563         }
15564    };   
15565 }();/*
15566  * Based on:
15567  * Ext JS Library 1.1.1
15568  * Copyright(c) 2006-2007, Ext JS, LLC.
15569  *
15570  * Originally Released Under LGPL - original licence link has changed is not relivant.
15571  *
15572  * Fork - LGPL
15573  * <script type="text/javascript">
15574  */
15575
15576  
15577
15578 /**
15579  * @class Roo.util.ClickRepeater
15580  * @extends Roo.util.Observable
15581  * 
15582  * A wrapper class which can be applied to any element. Fires a "click" event while the
15583  * mouse is pressed. The interval between firings may be specified in the config but
15584  * defaults to 10 milliseconds.
15585  * 
15586  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15587  * 
15588  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15589  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15590  * Similar to an autorepeat key delay.
15591  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15592  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15593  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15594  *           "interval" and "delay" are ignored. "immediate" is honored.
15595  * @cfg {Boolean} preventDefault True to prevent the default click event
15596  * @cfg {Boolean} stopDefault True to stop the default click event
15597  * 
15598  * @history
15599  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15600  *     2007-02-02 jvs Renamed to ClickRepeater
15601  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15602  *
15603  *  @constructor
15604  * @param {String/HTMLElement/Element} el The element to listen on
15605  * @param {Object} config
15606  **/
15607 Roo.util.ClickRepeater = function(el, config)
15608 {
15609     this.el = Roo.get(el);
15610     this.el.unselectable();
15611
15612     Roo.apply(this, config);
15613
15614     this.addEvents({
15615     /**
15616      * @event mousedown
15617      * Fires when the mouse button is depressed.
15618      * @param {Roo.util.ClickRepeater} this
15619      */
15620         "mousedown" : true,
15621     /**
15622      * @event click
15623      * Fires on a specified interval during the time the element is pressed.
15624      * @param {Roo.util.ClickRepeater} this
15625      */
15626         "click" : true,
15627     /**
15628      * @event mouseup
15629      * Fires when the mouse key is released.
15630      * @param {Roo.util.ClickRepeater} this
15631      */
15632         "mouseup" : true
15633     });
15634
15635     this.el.on("mousedown", this.handleMouseDown, this);
15636     if(this.preventDefault || this.stopDefault){
15637         this.el.on("click", function(e){
15638             if(this.preventDefault){
15639                 e.preventDefault();
15640             }
15641             if(this.stopDefault){
15642                 e.stopEvent();
15643             }
15644         }, this);
15645     }
15646
15647     // allow inline handler
15648     if(this.handler){
15649         this.on("click", this.handler,  this.scope || this);
15650     }
15651
15652     Roo.util.ClickRepeater.superclass.constructor.call(this);
15653 };
15654
15655 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15656     interval : 20,
15657     delay: 250,
15658     preventDefault : true,
15659     stopDefault : false,
15660     timer : 0,
15661
15662     // private
15663     handleMouseDown : function(){
15664         clearTimeout(this.timer);
15665         this.el.blur();
15666         if(this.pressClass){
15667             this.el.addClass(this.pressClass);
15668         }
15669         this.mousedownTime = new Date();
15670
15671         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15672         this.el.on("mouseout", this.handleMouseOut, this);
15673
15674         this.fireEvent("mousedown", this);
15675         this.fireEvent("click", this);
15676         
15677         this.timer = this.click.defer(this.delay || this.interval, this);
15678     },
15679
15680     // private
15681     click : function(){
15682         this.fireEvent("click", this);
15683         this.timer = this.click.defer(this.getInterval(), this);
15684     },
15685
15686     // private
15687     getInterval: function(){
15688         if(!this.accelerate){
15689             return this.interval;
15690         }
15691         var pressTime = this.mousedownTime.getElapsed();
15692         if(pressTime < 500){
15693             return 400;
15694         }else if(pressTime < 1700){
15695             return 320;
15696         }else if(pressTime < 2600){
15697             return 250;
15698         }else if(pressTime < 3500){
15699             return 180;
15700         }else if(pressTime < 4400){
15701             return 140;
15702         }else if(pressTime < 5300){
15703             return 80;
15704         }else if(pressTime < 6200){
15705             return 50;
15706         }else{
15707             return 10;
15708         }
15709     },
15710
15711     // private
15712     handleMouseOut : function(){
15713         clearTimeout(this.timer);
15714         if(this.pressClass){
15715             this.el.removeClass(this.pressClass);
15716         }
15717         this.el.on("mouseover", this.handleMouseReturn, this);
15718     },
15719
15720     // private
15721     handleMouseReturn : function(){
15722         this.el.un("mouseover", this.handleMouseReturn);
15723         if(this.pressClass){
15724             this.el.addClass(this.pressClass);
15725         }
15726         this.click();
15727     },
15728
15729     // private
15730     handleMouseUp : function(){
15731         clearTimeout(this.timer);
15732         this.el.un("mouseover", this.handleMouseReturn);
15733         this.el.un("mouseout", this.handleMouseOut);
15734         Roo.get(document).un("mouseup", this.handleMouseUp);
15735         this.el.removeClass(this.pressClass);
15736         this.fireEvent("mouseup", this);
15737     }
15738 });/**
15739  * @class Roo.util.Clipboard
15740  * @static
15741  * 
15742  * Clipboard UTILS
15743  * 
15744  **/
15745 Roo.util.Clipboard = {
15746     /**
15747      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15748      * @param {String} text to copy to clipboard
15749      */
15750     write : function(text) {
15751         // navigator clipboard api needs a secure context (https)
15752         if (navigator.clipboard && window.isSecureContext) {
15753             // navigator clipboard api method'
15754             navigator.clipboard.writeText(text);
15755             return ;
15756         } 
15757         // text area method
15758         var ta = document.createElement("textarea");
15759         ta.value = text;
15760         // make the textarea out of viewport
15761         ta.style.position = "fixed";
15762         ta.style.left = "-999999px";
15763         ta.style.top = "-999999px";
15764         document.body.appendChild(ta);
15765         ta.focus();
15766         ta.select();
15767         document.execCommand('copy');
15768         (function() {
15769             ta.remove();
15770         }).defer(100);
15771         
15772     }
15773         
15774 }
15775     /*
15776  * Based on:
15777  * Ext JS Library 1.1.1
15778  * Copyright(c) 2006-2007, Ext JS, LLC.
15779  *
15780  * Originally Released Under LGPL - original licence link has changed is not relivant.
15781  *
15782  * Fork - LGPL
15783  * <script type="text/javascript">
15784  */
15785
15786  
15787 /**
15788  * @class Roo.KeyNav
15789  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15790  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15791  * way to implement custom navigation schemes for any UI component.</p>
15792  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15793  * pageUp, pageDown, del, home, end.  Usage:</p>
15794  <pre><code>
15795 var nav = new Roo.KeyNav("my-element", {
15796     "left" : function(e){
15797         this.moveLeft(e.ctrlKey);
15798     },
15799     "right" : function(e){
15800         this.moveRight(e.ctrlKey);
15801     },
15802     "enter" : function(e){
15803         this.save();
15804     },
15805     scope : this
15806 });
15807 </code></pre>
15808  * @constructor
15809  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15810  * @param {Object} config The config
15811  */
15812 Roo.KeyNav = function(el, config){
15813     this.el = Roo.get(el);
15814     Roo.apply(this, config);
15815     if(!this.disabled){
15816         this.disabled = true;
15817         this.enable();
15818     }
15819 };
15820
15821 Roo.KeyNav.prototype = {
15822     /**
15823      * @cfg {Boolean} disabled
15824      * True to disable this KeyNav instance (defaults to false)
15825      */
15826     disabled : false,
15827     /**
15828      * @cfg {String} defaultEventAction
15829      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15830      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15831      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15832      */
15833     defaultEventAction: "stopEvent",
15834     /**
15835      * @cfg {Boolean} forceKeyDown
15836      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15837      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15838      * handle keydown instead of keypress.
15839      */
15840     forceKeyDown : false,
15841
15842     // private
15843     prepareEvent : function(e){
15844         var k = e.getKey();
15845         var h = this.keyToHandler[k];
15846         //if(h && this[h]){
15847         //    e.stopPropagation();
15848         //}
15849         if(Roo.isSafari && h && k >= 37 && k <= 40){
15850             e.stopEvent();
15851         }
15852     },
15853
15854     // private
15855     relay : function(e){
15856         var k = e.getKey();
15857         var h = this.keyToHandler[k];
15858         if(h && this[h]){
15859             if(this.doRelay(e, this[h], h) !== true){
15860                 e[this.defaultEventAction]();
15861             }
15862         }
15863     },
15864
15865     // private
15866     doRelay : function(e, h, hname){
15867         return h.call(this.scope || this, e);
15868     },
15869
15870     // possible handlers
15871     enter : false,
15872     left : false,
15873     right : false,
15874     up : false,
15875     down : false,
15876     tab : false,
15877     esc : false,
15878     pageUp : false,
15879     pageDown : false,
15880     del : false,
15881     home : false,
15882     end : false,
15883
15884     // quick lookup hash
15885     keyToHandler : {
15886         37 : "left",
15887         39 : "right",
15888         38 : "up",
15889         40 : "down",
15890         33 : "pageUp",
15891         34 : "pageDown",
15892         46 : "del",
15893         36 : "home",
15894         35 : "end",
15895         13 : "enter",
15896         27 : "esc",
15897         9  : "tab"
15898     },
15899
15900         /**
15901          * Enable this KeyNav
15902          */
15903         enable: function(){
15904                 if(this.disabled){
15905             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15906             // the EventObject will normalize Safari automatically
15907             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15908                 this.el.on("keydown", this.relay,  this);
15909             }else{
15910                 this.el.on("keydown", this.prepareEvent,  this);
15911                 this.el.on("keypress", this.relay,  this);
15912             }
15913                     this.disabled = false;
15914                 }
15915         },
15916
15917         /**
15918          * Disable this KeyNav
15919          */
15920         disable: function(){
15921                 if(!this.disabled){
15922                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15923                 this.el.un("keydown", this.relay);
15924             }else{
15925                 this.el.un("keydown", this.prepareEvent);
15926                 this.el.un("keypress", this.relay);
15927             }
15928                     this.disabled = true;
15929                 }
15930         }
15931 };/*
15932  * Based on:
15933  * Ext JS Library 1.1.1
15934  * Copyright(c) 2006-2007, Ext JS, LLC.
15935  *
15936  * Originally Released Under LGPL - original licence link has changed is not relivant.
15937  *
15938  * Fork - LGPL
15939  * <script type="text/javascript">
15940  */
15941
15942  
15943 /**
15944  * @class Roo.KeyMap
15945  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15946  * The constructor accepts the same config object as defined by {@link #addBinding}.
15947  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15948  * combination it will call the function with this signature (if the match is a multi-key
15949  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15950  * A KeyMap can also handle a string representation of keys.<br />
15951  * Usage:
15952  <pre><code>
15953 // map one key by key code
15954 var map = new Roo.KeyMap("my-element", {
15955     key: 13, // or Roo.EventObject.ENTER
15956     fn: myHandler,
15957     scope: myObject
15958 });
15959
15960 // map multiple keys to one action by string
15961 var map = new Roo.KeyMap("my-element", {
15962     key: "a\r\n\t",
15963     fn: myHandler,
15964     scope: myObject
15965 });
15966
15967 // map multiple keys to multiple actions by strings and array of codes
15968 var map = new Roo.KeyMap("my-element", [
15969     {
15970         key: [10,13],
15971         fn: function(){ alert("Return was pressed"); }
15972     }, {
15973         key: "abc",
15974         fn: function(){ alert('a, b or c was pressed'); }
15975     }, {
15976         key: "\t",
15977         ctrl:true,
15978         shift:true,
15979         fn: function(){ alert('Control + shift + tab was pressed.'); }
15980     }
15981 ]);
15982 </code></pre>
15983  * <b>Note: A KeyMap starts enabled</b>
15984  * @constructor
15985  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15986  * @param {Object} config The config (see {@link #addBinding})
15987  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15988  */
15989 Roo.KeyMap = function(el, config, eventName){
15990     this.el  = Roo.get(el);
15991     this.eventName = eventName || "keydown";
15992     this.bindings = [];
15993     if(config){
15994         this.addBinding(config);
15995     }
15996     this.enable();
15997 };
15998
15999 Roo.KeyMap.prototype = {
16000     /**
16001      * True to stop the event from bubbling and prevent the default browser action if the
16002      * key was handled by the KeyMap (defaults to false)
16003      * @type Boolean
16004      */
16005     stopEvent : false,
16006
16007     /**
16008      * Add a new binding to this KeyMap. The following config object properties are supported:
16009      * <pre>
16010 Property    Type             Description
16011 ----------  ---------------  ----------------------------------------------------------------------
16012 key         String/Array     A single keycode or an array of keycodes to handle
16013 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
16014 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
16015 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16016 fn          Function         The function to call when KeyMap finds the expected key combination
16017 scope       Object           The scope of the callback function
16018 </pre>
16019      *
16020      * Usage:
16021      * <pre><code>
16022 // Create a KeyMap
16023 var map = new Roo.KeyMap(document, {
16024     key: Roo.EventObject.ENTER,
16025     fn: handleKey,
16026     scope: this
16027 });
16028
16029 //Add a new binding to the existing KeyMap later
16030 map.addBinding({
16031     key: 'abc',
16032     shift: true,
16033     fn: handleKey,
16034     scope: this
16035 });
16036 </code></pre>
16037      * @param {Object/Array} config A single KeyMap config or an array of configs
16038      */
16039         addBinding : function(config){
16040         if(config instanceof Array){
16041             for(var i = 0, len = config.length; i < len; i++){
16042                 this.addBinding(config[i]);
16043             }
16044             return;
16045         }
16046         var keyCode = config.key,
16047             shift = config.shift, 
16048             ctrl = config.ctrl, 
16049             alt = config.alt,
16050             fn = config.fn,
16051             scope = config.scope;
16052         if(typeof keyCode == "string"){
16053             var ks = [];
16054             var keyString = keyCode.toUpperCase();
16055             for(var j = 0, len = keyString.length; j < len; j++){
16056                 ks.push(keyString.charCodeAt(j));
16057             }
16058             keyCode = ks;
16059         }
16060         var keyArray = keyCode instanceof Array;
16061         var handler = function(e){
16062             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16063                 var k = e.getKey();
16064                 if(keyArray){
16065                     for(var i = 0, len = keyCode.length; i < len; i++){
16066                         if(keyCode[i] == k){
16067                           if(this.stopEvent){
16068                               e.stopEvent();
16069                           }
16070                           fn.call(scope || window, k, e);
16071                           return;
16072                         }
16073                     }
16074                 }else{
16075                     if(k == keyCode){
16076                         if(this.stopEvent){
16077                            e.stopEvent();
16078                         }
16079                         fn.call(scope || window, k, e);
16080                     }
16081                 }
16082             }
16083         };
16084         this.bindings.push(handler);  
16085         },
16086
16087     /**
16088      * Shorthand for adding a single key listener
16089      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16090      * following options:
16091      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16092      * @param {Function} fn The function to call
16093      * @param {Object} scope (optional) The scope of the function
16094      */
16095     on : function(key, fn, scope){
16096         var keyCode, shift, ctrl, alt;
16097         if(typeof key == "object" && !(key instanceof Array)){
16098             keyCode = key.key;
16099             shift = key.shift;
16100             ctrl = key.ctrl;
16101             alt = key.alt;
16102         }else{
16103             keyCode = key;
16104         }
16105         this.addBinding({
16106             key: keyCode,
16107             shift: shift,
16108             ctrl: ctrl,
16109             alt: alt,
16110             fn: fn,
16111             scope: scope
16112         })
16113     },
16114
16115     // private
16116     handleKeyDown : function(e){
16117             if(this.enabled){ //just in case
16118             var b = this.bindings;
16119             for(var i = 0, len = b.length; i < len; i++){
16120                 b[i].call(this, e);
16121             }
16122             }
16123         },
16124         
16125         /**
16126          * Returns true if this KeyMap is enabled
16127          * @return {Boolean} 
16128          */
16129         isEnabled : function(){
16130             return this.enabled;  
16131         },
16132         
16133         /**
16134          * Enables this KeyMap
16135          */
16136         enable: function(){
16137                 if(!this.enabled){
16138                     this.el.on(this.eventName, this.handleKeyDown, this);
16139                     this.enabled = true;
16140                 }
16141         },
16142
16143         /**
16144          * Disable this KeyMap
16145          */
16146         disable: function(){
16147                 if(this.enabled){
16148                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16149                     this.enabled = false;
16150                 }
16151         }
16152 };/*
16153  * Based on:
16154  * Ext JS Library 1.1.1
16155  * Copyright(c) 2006-2007, Ext JS, LLC.
16156  *
16157  * Originally Released Under LGPL - original licence link has changed is not relivant.
16158  *
16159  * Fork - LGPL
16160  * <script type="text/javascript">
16161  */
16162
16163  
16164 /**
16165  * @class Roo.util.TextMetrics
16166  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16167  * wide, in pixels, a given block of text will be.
16168  * @static
16169  */
16170 Roo.util.TextMetrics = function(){
16171     var shared;
16172     return {
16173         /**
16174          * Measures the size of the specified text
16175          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16176          * that can affect the size of the rendered text
16177          * @param {String} text The text to measure
16178          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16179          * in order to accurately measure the text height
16180          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16181          */
16182         measure : function(el, text, fixedWidth){
16183             if(!shared){
16184                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16185             }
16186             shared.bind(el);
16187             shared.setFixedWidth(fixedWidth || 'auto');
16188             return shared.getSize(text);
16189         },
16190
16191         /**
16192          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16193          * the overhead of multiple calls to initialize the style properties on each measurement.
16194          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16195          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16196          * in order to accurately measure the text height
16197          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16198          */
16199         createInstance : function(el, fixedWidth){
16200             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16201         }
16202     };
16203 }();
16204
16205 /**
16206  * @class Roo.util.TextMetrics.Instance
16207  * Instance of  TextMetrics Calcuation
16208  * @constructor
16209  * Create a new TextMetrics Instance
16210  * @param {Object} bindto
16211  * @param {Boolean} fixedWidth
16212  */
16213
16214 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16215 {
16216     var ml = new Roo.Element(document.createElement('div'));
16217     document.body.appendChild(ml.dom);
16218     ml.position('absolute');
16219     ml.setLeftTop(-1000, -1000);
16220     ml.hide();
16221
16222     if(fixedWidth){
16223         ml.setWidth(fixedWidth);
16224     }
16225      
16226     var instance = {
16227         /**
16228          * Returns the size of the specified text based on the internal element's style and width properties
16229          * @param {String} text The text to measure
16230          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16231          */
16232         getSize : function(text){
16233             ml.update(text);
16234             var s = ml.getSize();
16235             ml.update('');
16236             return s;
16237         },
16238
16239         /**
16240          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16241          * that can affect the size of the rendered text
16242          * @param {String/HTMLElement} el The element, dom node or id
16243          */
16244         bind : function(el){
16245             ml.setStyle(
16246                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16247             );
16248         },
16249
16250         /**
16251          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16252          * to set a fixed width in order to accurately measure the text height.
16253          * @param {Number} width The width to set on the element
16254          */
16255         setFixedWidth : function(width){
16256             ml.setWidth(width);
16257         },
16258
16259         /**
16260          * Returns the measured width of the specified text
16261          * @param {String} text The text to measure
16262          * @return {Number} width The width in pixels
16263          */
16264         getWidth : function(text){
16265             ml.dom.style.width = 'auto';
16266             return this.getSize(text).width;
16267         },
16268
16269         /**
16270          * Returns the measured height of the specified text.  For multiline text, be sure to call
16271          * {@link #setFixedWidth} if necessary.
16272          * @param {String} text The text to measure
16273          * @return {Number} height The height in pixels
16274          */
16275         getHeight : function(text){
16276             return this.getSize(text).height;
16277         }
16278     };
16279
16280     instance.bind(bindTo);
16281
16282     return instance;
16283 };
16284
16285 // backwards compat
16286 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16287  * Based on:
16288  * Ext JS Library 1.1.1
16289  * Copyright(c) 2006-2007, Ext JS, LLC.
16290  *
16291  * Originally Released Under LGPL - original licence link has changed is not relivant.
16292  *
16293  * Fork - LGPL
16294  * <script type="text/javascript">
16295  */
16296
16297 /**
16298  * @class Roo.state.Provider
16299  * Abstract base class for state provider implementations. This class provides methods
16300  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16301  * Provider interface.
16302  */
16303 Roo.state.Provider = function(){
16304     /**
16305      * @event statechange
16306      * Fires when a state change occurs.
16307      * @param {Provider} this This state provider
16308      * @param {String} key The state key which was changed
16309      * @param {String} value The encoded value for the state
16310      */
16311     this.addEvents({
16312         "statechange": true
16313     });
16314     this.state = {};
16315     Roo.state.Provider.superclass.constructor.call(this);
16316 };
16317 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16318     /**
16319      * Returns the current value for a key
16320      * @param {String} name The key name
16321      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16322      * @return {Mixed} The state data
16323      */
16324     get : function(name, defaultValue){
16325         return typeof this.state[name] == "undefined" ?
16326             defaultValue : this.state[name];
16327     },
16328     
16329     /**
16330      * Clears a value from the state
16331      * @param {String} name The key name
16332      */
16333     clear : function(name){
16334         delete this.state[name];
16335         this.fireEvent("statechange", this, name, null);
16336     },
16337     
16338     /**
16339      * Sets the value for a key
16340      * @param {String} name The key name
16341      * @param {Mixed} value The value to set
16342      */
16343     set : function(name, value){
16344         this.state[name] = value;
16345         this.fireEvent("statechange", this, name, value);
16346     },
16347     
16348     /**
16349      * Decodes a string previously encoded with {@link #encodeValue}.
16350      * @param {String} value The value to decode
16351      * @return {Mixed} The decoded value
16352      */
16353     decodeValue : function(cookie){
16354         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16355         var matches = re.exec(unescape(cookie));
16356         if(!matches || !matches[1]) {
16357             return; // non state cookie
16358         }
16359         var type = matches[1];
16360         var v = matches[2];
16361         switch(type){
16362             case "n":
16363                 return parseFloat(v);
16364             case "d":
16365                 return new Date(Date.parse(v));
16366             case "b":
16367                 return (v == "1");
16368             case "a":
16369                 var all = [];
16370                 var values = v.split("^");
16371                 for(var i = 0, len = values.length; i < len; i++){
16372                     all.push(this.decodeValue(values[i]));
16373                 }
16374                 return all;
16375            case "o":
16376                 var all = {};
16377                 var values = v.split("^");
16378                 for(var i = 0, len = values.length; i < len; i++){
16379                     var kv = values[i].split("=");
16380                     all[kv[0]] = this.decodeValue(kv[1]);
16381                 }
16382                 return all;
16383            default:
16384                 return v;
16385         }
16386     },
16387     
16388     /**
16389      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16390      * @param {Mixed} value The value to encode
16391      * @return {String} The encoded value
16392      */
16393     encodeValue : function(v){
16394         var enc;
16395         if(typeof v == "number"){
16396             enc = "n:" + v;
16397         }else if(typeof v == "boolean"){
16398             enc = "b:" + (v ? "1" : "0");
16399         }else if(v instanceof Date){
16400             enc = "d:" + v.toGMTString();
16401         }else if(v instanceof Array){
16402             var flat = "";
16403             for(var i = 0, len = v.length; i < len; i++){
16404                 flat += this.encodeValue(v[i]);
16405                 if(i != len-1) {
16406                     flat += "^";
16407                 }
16408             }
16409             enc = "a:" + flat;
16410         }else if(typeof v == "object"){
16411             var flat = "";
16412             for(var key in v){
16413                 if(typeof v[key] != "function"){
16414                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16415                 }
16416             }
16417             enc = "o:" + flat.substring(0, flat.length-1);
16418         }else{
16419             enc = "s:" + v;
16420         }
16421         return escape(enc);        
16422     }
16423 });
16424
16425 /*
16426  * Based on:
16427  * Ext JS Library 1.1.1
16428  * Copyright(c) 2006-2007, Ext JS, LLC.
16429  *
16430  * Originally Released Under LGPL - original licence link has changed is not relivant.
16431  *
16432  * Fork - LGPL
16433  * <script type="text/javascript">
16434  */
16435 /**
16436  * @class Roo.state.Manager
16437  * This is the global state manager. By default all components that are "state aware" check this class
16438  * for state information if you don't pass them a custom state provider. In order for this class
16439  * to be useful, it must be initialized with a provider when your application initializes.
16440  <pre><code>
16441 // in your initialization function
16442 init : function(){
16443    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16444    ...
16445    // supposed you have a {@link Roo.BorderLayout}
16446    var layout = new Roo.BorderLayout(...);
16447    layout.restoreState();
16448    // or a {Roo.BasicDialog}
16449    var dialog = new Roo.BasicDialog(...);
16450    dialog.restoreState();
16451  </code></pre>
16452  * @static
16453  */
16454 Roo.state.Manager = function(){
16455     var provider = new Roo.state.Provider();
16456     
16457     return {
16458         /**
16459          * Configures the default state provider for your application
16460          * @param {Provider} stateProvider The state provider to set
16461          */
16462         setProvider : function(stateProvider){
16463             provider = stateProvider;
16464         },
16465         
16466         /**
16467          * Returns the current value for a key
16468          * @param {String} name The key name
16469          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16470          * @return {Mixed} The state data
16471          */
16472         get : function(key, defaultValue){
16473             return provider.get(key, defaultValue);
16474         },
16475         
16476         /**
16477          * Sets the value for a key
16478          * @param {String} name The key name
16479          * @param {Mixed} value The state data
16480          */
16481          set : function(key, value){
16482             provider.set(key, value);
16483         },
16484         
16485         /**
16486          * Clears a value from the state
16487          * @param {String} name The key name
16488          */
16489         clear : function(key){
16490             provider.clear(key);
16491         },
16492         
16493         /**
16494          * Gets the currently configured state provider
16495          * @return {Provider} The state provider
16496          */
16497         getProvider : function(){
16498             return provider;
16499         }
16500     };
16501 }();
16502 /*
16503  * Based on:
16504  * Ext JS Library 1.1.1
16505  * Copyright(c) 2006-2007, Ext JS, LLC.
16506  *
16507  * Originally Released Under LGPL - original licence link has changed is not relivant.
16508  *
16509  * Fork - LGPL
16510  * <script type="text/javascript">
16511  */
16512 /**
16513  * @class Roo.state.CookieProvider
16514  * @extends Roo.state.Provider
16515  * The default Provider implementation which saves state via cookies.
16516  * <br />Usage:
16517  <pre><code>
16518    var cp = new Roo.state.CookieProvider({
16519        path: "/cgi-bin/",
16520        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16521        domain: "roojs.com"
16522    })
16523    Roo.state.Manager.setProvider(cp);
16524  </code></pre>
16525  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16526  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16527  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16528  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16529  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16530  * domain the page is running on including the 'www' like 'www.roojs.com')
16531  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16532  * @constructor
16533  * Create a new CookieProvider
16534  * @param {Object} config The configuration object
16535  */
16536 Roo.state.CookieProvider = function(config){
16537     Roo.state.CookieProvider.superclass.constructor.call(this);
16538     this.path = "/";
16539     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16540     this.domain = null;
16541     this.secure = false;
16542     Roo.apply(this, config);
16543     this.state = this.readCookies();
16544 };
16545
16546 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16547     // private
16548     set : function(name, value){
16549         if(typeof value == "undefined" || value === null){
16550             this.clear(name);
16551             return;
16552         }
16553         this.setCookie(name, value);
16554         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16555     },
16556
16557     // private
16558     clear : function(name){
16559         this.clearCookie(name);
16560         Roo.state.CookieProvider.superclass.clear.call(this, name);
16561     },
16562
16563     // private
16564     readCookies : function(){
16565         var cookies = {};
16566         var c = document.cookie + ";";
16567         var re = /\s?(.*?)=(.*?);/g;
16568         var matches;
16569         while((matches = re.exec(c)) != null){
16570             var name = matches[1];
16571             var value = matches[2];
16572             if(name && name.substring(0,3) == "ys-"){
16573                 cookies[name.substr(3)] = this.decodeValue(value);
16574             }
16575         }
16576         return cookies;
16577     },
16578
16579     // private
16580     setCookie : function(name, value){
16581         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16582            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16583            ((this.path == null) ? "" : ("; path=" + this.path)) +
16584            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16585            ((this.secure == true) ? "; secure" : "");
16586     },
16587
16588     // private
16589     clearCookie : function(name){
16590         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16591            ((this.path == null) ? "" : ("; path=" + this.path)) +
16592            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16593            ((this.secure == true) ? "; secure" : "");
16594     }
16595 });/*
16596  * Based on:
16597  * Ext JS Library 1.1.1
16598  * Copyright(c) 2006-2007, Ext JS, LLC.
16599  *
16600  * Originally Released Under LGPL - original licence link has changed is not relivant.
16601  *
16602  * Fork - LGPL
16603  * <script type="text/javascript">
16604  */
16605  
16606
16607 /**
16608  * @class Roo.ComponentMgr
16609  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16610  * @static
16611  */
16612 Roo.ComponentMgr = function(){
16613     var all = new Roo.util.MixedCollection();
16614
16615     return {
16616         /**
16617          * Registers a component.
16618          * @param {Roo.Component} c The component
16619          */
16620         register : function(c){
16621             all.add(c);
16622         },
16623
16624         /**
16625          * Unregisters a component.
16626          * @param {Roo.Component} c The component
16627          */
16628         unregister : function(c){
16629             all.remove(c);
16630         },
16631
16632         /**
16633          * Returns a component by id
16634          * @param {String} id The component id
16635          */
16636         get : function(id){
16637             return all.get(id);
16638         },
16639
16640         /**
16641          * Registers a function that will be called when a specified component is added to ComponentMgr
16642          * @param {String} id The component id
16643          * @param {Funtction} fn The callback function
16644          * @param {Object} scope The scope of the callback
16645          */
16646         onAvailable : function(id, fn, scope){
16647             all.on("add", function(index, o){
16648                 if(o.id == id){
16649                     fn.call(scope || o, o);
16650                     all.un("add", fn, scope);
16651                 }
16652             });
16653         }
16654     };
16655 }();/*
16656  * Based on:
16657  * Ext JS Library 1.1.1
16658  * Copyright(c) 2006-2007, Ext JS, LLC.
16659  *
16660  * Originally Released Under LGPL - original licence link has changed is not relivant.
16661  *
16662  * Fork - LGPL
16663  * <script type="text/javascript">
16664  */
16665  
16666 /**
16667  * @class Roo.Component
16668  * @extends Roo.util.Observable
16669  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16670  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16671  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16672  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16673  * All visual components (widgets) that require rendering into a layout should subclass Component.
16674  * @constructor
16675  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16676  * 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
16677  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16678  */
16679 Roo.Component = function(config){
16680     config = config || {};
16681     if(config.tagName || config.dom || typeof config == "string"){ // element object
16682         config = {el: config, id: config.id || config};
16683     }
16684     this.initialConfig = config;
16685
16686     Roo.apply(this, config);
16687     this.addEvents({
16688         /**
16689          * @event disable
16690          * Fires after the component is disabled.
16691              * @param {Roo.Component} this
16692              */
16693         disable : true,
16694         /**
16695          * @event enable
16696          * Fires after the component is enabled.
16697              * @param {Roo.Component} this
16698              */
16699         enable : true,
16700         /**
16701          * @event beforeshow
16702          * Fires before the component is shown.  Return false to stop the show.
16703              * @param {Roo.Component} this
16704              */
16705         beforeshow : true,
16706         /**
16707          * @event show
16708          * Fires after the component is shown.
16709              * @param {Roo.Component} this
16710              */
16711         show : true,
16712         /**
16713          * @event beforehide
16714          * Fires before the component is hidden. Return false to stop the hide.
16715              * @param {Roo.Component} this
16716              */
16717         beforehide : true,
16718         /**
16719          * @event hide
16720          * Fires after the component is hidden.
16721              * @param {Roo.Component} this
16722              */
16723         hide : true,
16724         /**
16725          * @event beforerender
16726          * Fires before the component is rendered. Return false to stop the render.
16727              * @param {Roo.Component} this
16728              */
16729         beforerender : true,
16730         /**
16731          * @event render
16732          * Fires after the component is rendered.
16733              * @param {Roo.Component} this
16734              */
16735         render : true,
16736         /**
16737          * @event beforedestroy
16738          * Fires before the component is destroyed. Return false to stop the destroy.
16739              * @param {Roo.Component} this
16740              */
16741         beforedestroy : true,
16742         /**
16743          * @event destroy
16744          * Fires after the component is destroyed.
16745              * @param {Roo.Component} this
16746              */
16747         destroy : true
16748     });
16749     if(!this.id){
16750         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16751     }
16752     Roo.ComponentMgr.register(this);
16753     Roo.Component.superclass.constructor.call(this);
16754     this.initComponent();
16755     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16756         this.render(this.renderTo);
16757         delete this.renderTo;
16758     }
16759 };
16760
16761 /** @private */
16762 Roo.Component.AUTO_ID = 1000;
16763
16764 Roo.extend(Roo.Component, Roo.util.Observable, {
16765     /**
16766      * @scope Roo.Component.prototype
16767      * @type {Boolean}
16768      * true if this component is hidden. Read-only.
16769      */
16770     hidden : false,
16771     /**
16772      * @type {Boolean}
16773      * true if this component is disabled. Read-only.
16774      */
16775     disabled : false,
16776     /**
16777      * @type {Boolean}
16778      * true if this component has been rendered. Read-only.
16779      */
16780     rendered : false,
16781     
16782     /** @cfg {String} disableClass
16783      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16784      */
16785     disabledClass : "x-item-disabled",
16786         /** @cfg {Boolean} allowDomMove
16787          * Whether the component can move the Dom node when rendering (defaults to true).
16788          */
16789     allowDomMove : true,
16790     /** @cfg {String} hideMode (display|visibility)
16791      * How this component should hidden. Supported values are
16792      * "visibility" (css visibility), "offsets" (negative offset position) and
16793      * "display" (css display) - defaults to "display".
16794      */
16795     hideMode: 'display',
16796
16797     /** @private */
16798     ctype : "Roo.Component",
16799
16800     /**
16801      * @cfg {String} actionMode 
16802      * which property holds the element that used for  hide() / show() / disable() / enable()
16803      * default is 'el' for forms you probably want to set this to fieldEl 
16804      */
16805     actionMode : "el",
16806
16807     /** @private */
16808     getActionEl : function(){
16809         return this[this.actionMode];
16810     },
16811
16812     initComponent : Roo.emptyFn,
16813     /**
16814      * If this is a lazy rendering component, render it to its container element.
16815      * @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.
16816      */
16817     render : function(container, position){
16818         
16819         if(this.rendered){
16820             return this;
16821         }
16822         
16823         if(this.fireEvent("beforerender", this) === false){
16824             return false;
16825         }
16826         
16827         if(!container && this.el){
16828             this.el = Roo.get(this.el);
16829             container = this.el.dom.parentNode;
16830             this.allowDomMove = false;
16831         }
16832         this.container = Roo.get(container);
16833         this.rendered = true;
16834         if(position !== undefined){
16835             if(typeof position == 'number'){
16836                 position = this.container.dom.childNodes[position];
16837             }else{
16838                 position = Roo.getDom(position);
16839             }
16840         }
16841         this.onRender(this.container, position || null);
16842         if(this.cls){
16843             this.el.addClass(this.cls);
16844             delete this.cls;
16845         }
16846         if(this.style){
16847             this.el.applyStyles(this.style);
16848             delete this.style;
16849         }
16850         this.fireEvent("render", this);
16851         this.afterRender(this.container);
16852         if(this.hidden){
16853             this.hide();
16854         }
16855         if(this.disabled){
16856             this.disable();
16857         }
16858
16859         return this;
16860         
16861     },
16862
16863     /** @private */
16864     // default function is not really useful
16865     onRender : function(ct, position){
16866         if(this.el){
16867             this.el = Roo.get(this.el);
16868             if(this.allowDomMove !== false){
16869                 ct.dom.insertBefore(this.el.dom, position);
16870             }
16871         }
16872     },
16873
16874     /** @private */
16875     getAutoCreate : function(){
16876         var cfg = typeof this.autoCreate == "object" ?
16877                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16878         if(this.id && !cfg.id){
16879             cfg.id = this.id;
16880         }
16881         return cfg;
16882     },
16883
16884     /** @private */
16885     afterRender : Roo.emptyFn,
16886
16887     /**
16888      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16889      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16890      */
16891     destroy : function(){
16892         if(this.fireEvent("beforedestroy", this) !== false){
16893             this.purgeListeners();
16894             this.beforeDestroy();
16895             if(this.rendered){
16896                 this.el.removeAllListeners();
16897                 this.el.remove();
16898                 if(this.actionMode == "container"){
16899                     this.container.remove();
16900                 }
16901             }
16902             this.onDestroy();
16903             Roo.ComponentMgr.unregister(this);
16904             this.fireEvent("destroy", this);
16905         }
16906     },
16907
16908         /** @private */
16909     beforeDestroy : function(){
16910
16911     },
16912
16913         /** @private */
16914         onDestroy : function(){
16915
16916     },
16917
16918     /**
16919      * Returns the underlying {@link Roo.Element}.
16920      * @return {Roo.Element} The element
16921      */
16922     getEl : function(){
16923         return this.el;
16924     },
16925
16926     /**
16927      * Returns the id of this component.
16928      * @return {String}
16929      */
16930     getId : function(){
16931         return this.id;
16932     },
16933
16934     /**
16935      * Try to focus this component.
16936      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16937      * @return {Roo.Component} this
16938      */
16939     focus : function(selectText){
16940         if(this.rendered){
16941             this.el.focus();
16942             if(selectText === true){
16943                 this.el.dom.select();
16944             }
16945         }
16946         return this;
16947     },
16948
16949     /** @private */
16950     blur : function(){
16951         if(this.rendered){
16952             this.el.blur();
16953         }
16954         return this;
16955     },
16956
16957     /**
16958      * Disable this component.
16959      * @return {Roo.Component} this
16960      */
16961     disable : function(){
16962         if(this.rendered){
16963             this.onDisable();
16964         }
16965         this.disabled = true;
16966         this.fireEvent("disable", this);
16967         return this;
16968     },
16969
16970         // private
16971     onDisable : function(){
16972         this.getActionEl().addClass(this.disabledClass);
16973         this.el.dom.disabled = true;
16974     },
16975
16976     /**
16977      * Enable this component.
16978      * @return {Roo.Component} this
16979      */
16980     enable : function(){
16981         if(this.rendered){
16982             this.onEnable();
16983         }
16984         this.disabled = false;
16985         this.fireEvent("enable", this);
16986         return this;
16987     },
16988
16989         // private
16990     onEnable : function(){
16991         this.getActionEl().removeClass(this.disabledClass);
16992         this.el.dom.disabled = false;
16993     },
16994
16995     /**
16996      * Convenience function for setting disabled/enabled by boolean.
16997      * @param {Boolean} disabled
16998      */
16999     setDisabled : function(disabled){
17000         this[disabled ? "disable" : "enable"]();
17001     },
17002
17003     /**
17004      * Show this component.
17005      * @return {Roo.Component} this
17006      */
17007     show: function(){
17008         if(this.fireEvent("beforeshow", this) !== false){
17009             this.hidden = false;
17010             if(this.rendered){
17011                 this.onShow();
17012             }
17013             this.fireEvent("show", this);
17014         }
17015         return this;
17016     },
17017
17018     // private
17019     onShow : function(){
17020         var ae = this.getActionEl();
17021         if(this.hideMode == 'visibility'){
17022             ae.dom.style.visibility = "visible";
17023         }else if(this.hideMode == 'offsets'){
17024             ae.removeClass('x-hidden');
17025         }else{
17026             ae.dom.style.display = "";
17027         }
17028     },
17029
17030     /**
17031      * Hide this component.
17032      * @return {Roo.Component} this
17033      */
17034     hide: function(){
17035         if(this.fireEvent("beforehide", this) !== false){
17036             this.hidden = true;
17037             if(this.rendered){
17038                 this.onHide();
17039             }
17040             this.fireEvent("hide", this);
17041         }
17042         return this;
17043     },
17044
17045     // private
17046     onHide : function(){
17047         var ae = this.getActionEl();
17048         if(this.hideMode == 'visibility'){
17049             ae.dom.style.visibility = "hidden";
17050         }else if(this.hideMode == 'offsets'){
17051             ae.addClass('x-hidden');
17052         }else{
17053             ae.dom.style.display = "none";
17054         }
17055     },
17056
17057     /**
17058      * Convenience function to hide or show this component by boolean.
17059      * @param {Boolean} visible True to show, false to hide
17060      * @return {Roo.Component} this
17061      */
17062     setVisible: function(visible){
17063         if(visible) {
17064             this.show();
17065         }else{
17066             this.hide();
17067         }
17068         return this;
17069     },
17070
17071     /**
17072      * Returns true if this component is visible.
17073      */
17074     isVisible : function(){
17075         return this.getActionEl().isVisible();
17076     },
17077
17078     cloneConfig : function(overrides){
17079         overrides = overrides || {};
17080         var id = overrides.id || Roo.id();
17081         var cfg = Roo.applyIf(overrides, this.initialConfig);
17082         cfg.id = id; // prevent dup id
17083         return new this.constructor(cfg);
17084     }
17085 });/*
17086  * Based on:
17087  * Ext JS Library 1.1.1
17088  * Copyright(c) 2006-2007, Ext JS, LLC.
17089  *
17090  * Originally Released Under LGPL - original licence link has changed is not relivant.
17091  *
17092  * Fork - LGPL
17093  * <script type="text/javascript">
17094  */
17095
17096 /**
17097  * @class Roo.BoxComponent
17098  * @extends Roo.Component
17099  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17100  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17101  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17102  * layout containers.
17103  * @constructor
17104  * @param {Roo.Element/String/Object} config The configuration options.
17105  */
17106 Roo.BoxComponent = function(config){
17107     Roo.Component.call(this, config);
17108     this.addEvents({
17109         /**
17110          * @event resize
17111          * Fires after the component is resized.
17112              * @param {Roo.Component} this
17113              * @param {Number} adjWidth The box-adjusted width that was set
17114              * @param {Number} adjHeight The box-adjusted height that was set
17115              * @param {Number} rawWidth The width that was originally specified
17116              * @param {Number} rawHeight The height that was originally specified
17117              */
17118         resize : true,
17119         /**
17120          * @event move
17121          * Fires after the component is moved.
17122              * @param {Roo.Component} this
17123              * @param {Number} x The new x position
17124              * @param {Number} y The new y position
17125              */
17126         move : true
17127     });
17128 };
17129
17130 Roo.extend(Roo.BoxComponent, Roo.Component, {
17131     // private, set in afterRender to signify that the component has been rendered
17132     boxReady : false,
17133     // private, used to defer height settings to subclasses
17134     deferHeight: false,
17135     /** @cfg {Number} width
17136      * width (optional) size of component
17137      */
17138      /** @cfg {Number} height
17139      * height (optional) size of component
17140      */
17141      
17142     /**
17143      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17144      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17145      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17146      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17147      * @return {Roo.BoxComponent} this
17148      */
17149     setSize : function(w, h){
17150         // support for standard size objects
17151         if(typeof w == 'object'){
17152             h = w.height;
17153             w = w.width;
17154         }
17155         // not rendered
17156         if(!this.boxReady){
17157             this.width = w;
17158             this.height = h;
17159             return this;
17160         }
17161
17162         // prevent recalcs when not needed
17163         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17164             return this;
17165         }
17166         this.lastSize = {width: w, height: h};
17167
17168         var adj = this.adjustSize(w, h);
17169         var aw = adj.width, ah = adj.height;
17170         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17171             var rz = this.getResizeEl();
17172             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17173                 rz.setSize(aw, ah);
17174             }else if(!this.deferHeight && ah !== undefined){
17175                 rz.setHeight(ah);
17176             }else if(aw !== undefined){
17177                 rz.setWidth(aw);
17178             }
17179             this.onResize(aw, ah, w, h);
17180             this.fireEvent('resize', this, aw, ah, w, h);
17181         }
17182         return this;
17183     },
17184
17185     /**
17186      * Gets the current size of the component's underlying element.
17187      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17188      */
17189     getSize : function(){
17190         return this.el.getSize();
17191     },
17192
17193     /**
17194      * Gets the current XY position of the component's underlying element.
17195      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17196      * @return {Array} The XY position of the element (e.g., [100, 200])
17197      */
17198     getPosition : function(local){
17199         if(local === true){
17200             return [this.el.getLeft(true), this.el.getTop(true)];
17201         }
17202         return this.xy || this.el.getXY();
17203     },
17204
17205     /**
17206      * Gets the current box measurements of the component's underlying element.
17207      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17208      * @returns {Object} box An object in the format {x, y, width, height}
17209      */
17210     getBox : function(local){
17211         var s = this.el.getSize();
17212         if(local){
17213             s.x = this.el.getLeft(true);
17214             s.y = this.el.getTop(true);
17215         }else{
17216             var xy = this.xy || this.el.getXY();
17217             s.x = xy[0];
17218             s.y = xy[1];
17219         }
17220         return s;
17221     },
17222
17223     /**
17224      * Sets the current box measurements of the component's underlying element.
17225      * @param {Object} box An object in the format {x, y, width, height}
17226      * @returns {Roo.BoxComponent} this
17227      */
17228     updateBox : function(box){
17229         this.setSize(box.width, box.height);
17230         this.setPagePosition(box.x, box.y);
17231         return this;
17232     },
17233
17234     // protected
17235     getResizeEl : function(){
17236         return this.resizeEl || this.el;
17237     },
17238
17239     // protected
17240     getPositionEl : function(){
17241         return this.positionEl || this.el;
17242     },
17243
17244     /**
17245      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17246      * This method fires the move event.
17247      * @param {Number} left The new left
17248      * @param {Number} top The new top
17249      * @returns {Roo.BoxComponent} this
17250      */
17251     setPosition : function(x, y){
17252         this.x = x;
17253         this.y = y;
17254         if(!this.boxReady){
17255             return this;
17256         }
17257         var adj = this.adjustPosition(x, y);
17258         var ax = adj.x, ay = adj.y;
17259
17260         var el = this.getPositionEl();
17261         if(ax !== undefined || ay !== undefined){
17262             if(ax !== undefined && ay !== undefined){
17263                 el.setLeftTop(ax, ay);
17264             }else if(ax !== undefined){
17265                 el.setLeft(ax);
17266             }else if(ay !== undefined){
17267                 el.setTop(ay);
17268             }
17269             this.onPosition(ax, ay);
17270             this.fireEvent('move', this, ax, ay);
17271         }
17272         return this;
17273     },
17274
17275     /**
17276      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17277      * This method fires the move event.
17278      * @param {Number} x The new x position
17279      * @param {Number} y The new y position
17280      * @returns {Roo.BoxComponent} this
17281      */
17282     setPagePosition : function(x, y){
17283         this.pageX = x;
17284         this.pageY = y;
17285         if(!this.boxReady){
17286             return;
17287         }
17288         if(x === undefined || y === undefined){ // cannot translate undefined points
17289             return;
17290         }
17291         var p = this.el.translatePoints(x, y);
17292         this.setPosition(p.left, p.top);
17293         return this;
17294     },
17295
17296     // private
17297     onRender : function(ct, position){
17298         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17299         if(this.resizeEl){
17300             this.resizeEl = Roo.get(this.resizeEl);
17301         }
17302         if(this.positionEl){
17303             this.positionEl = Roo.get(this.positionEl);
17304         }
17305     },
17306
17307     // private
17308     afterRender : function(){
17309         Roo.BoxComponent.superclass.afterRender.call(this);
17310         this.boxReady = true;
17311         this.setSize(this.width, this.height);
17312         if(this.x || this.y){
17313             this.setPosition(this.x, this.y);
17314         }
17315         if(this.pageX || this.pageY){
17316             this.setPagePosition(this.pageX, this.pageY);
17317         }
17318     },
17319
17320     /**
17321      * Force the component's size to recalculate based on the underlying element's current height and width.
17322      * @returns {Roo.BoxComponent} this
17323      */
17324     syncSize : function(){
17325         delete this.lastSize;
17326         this.setSize(this.el.getWidth(), this.el.getHeight());
17327         return this;
17328     },
17329
17330     /**
17331      * Called after the component is resized, this method is empty by default but can be implemented by any
17332      * subclass that needs to perform custom logic after a resize occurs.
17333      * @param {Number} adjWidth The box-adjusted width that was set
17334      * @param {Number} adjHeight The box-adjusted height that was set
17335      * @param {Number} rawWidth The width that was originally specified
17336      * @param {Number} rawHeight The height that was originally specified
17337      */
17338     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17339
17340     },
17341
17342     /**
17343      * Called after the component is moved, this method is empty by default but can be implemented by any
17344      * subclass that needs to perform custom logic after a move occurs.
17345      * @param {Number} x The new x position
17346      * @param {Number} y The new y position
17347      */
17348     onPosition : function(x, y){
17349
17350     },
17351
17352     // private
17353     adjustSize : function(w, h){
17354         if(this.autoWidth){
17355             w = 'auto';
17356         }
17357         if(this.autoHeight){
17358             h = 'auto';
17359         }
17360         return {width : w, height: h};
17361     },
17362
17363     // private
17364     adjustPosition : function(x, y){
17365         return {x : x, y: y};
17366     }
17367 });/*
17368  * Based on:
17369  * Ext JS Library 1.1.1
17370  * Copyright(c) 2006-2007, Ext JS, LLC.
17371  *
17372  * Originally Released Under LGPL - original licence link has changed is not relivant.
17373  *
17374  * Fork - LGPL
17375  * <script type="text/javascript">
17376  */
17377  (function(){ 
17378 /**
17379  * @class Roo.Layer
17380  * @extends Roo.Element
17381  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17382  * automatic maintaining of shadow/shim positions.
17383  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17384  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17385  * you can pass a string with a CSS class name. False turns off the shadow.
17386  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17387  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17388  * @cfg {String} cls CSS class to add to the element
17389  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17390  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17391  * @constructor
17392  * @param {Object} config An object with config options.
17393  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17394  */
17395
17396 Roo.Layer = function(config, existingEl){
17397     config = config || {};
17398     var dh = Roo.DomHelper;
17399     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17400     if(existingEl){
17401         this.dom = Roo.getDom(existingEl);
17402     }
17403     if(!this.dom){
17404         var o = config.dh || {tag: "div", cls: "x-layer"};
17405         this.dom = dh.append(pel, o);
17406     }
17407     if(config.cls){
17408         this.addClass(config.cls);
17409     }
17410     this.constrain = config.constrain !== false;
17411     this.visibilityMode = Roo.Element.VISIBILITY;
17412     if(config.id){
17413         this.id = this.dom.id = config.id;
17414     }else{
17415         this.id = Roo.id(this.dom);
17416     }
17417     this.zindex = config.zindex || this.getZIndex();
17418     this.position("absolute", this.zindex);
17419     if(config.shadow){
17420         this.shadowOffset = config.shadowOffset || 4;
17421         this.shadow = new Roo.Shadow({
17422             offset : this.shadowOffset,
17423             mode : config.shadow
17424         });
17425     }else{
17426         this.shadowOffset = 0;
17427     }
17428     this.useShim = config.shim !== false && Roo.useShims;
17429     this.useDisplay = config.useDisplay;
17430     this.hide();
17431 };
17432
17433 var supr = Roo.Element.prototype;
17434
17435 // shims are shared among layer to keep from having 100 iframes
17436 var shims = [];
17437
17438 Roo.extend(Roo.Layer, Roo.Element, {
17439
17440     getZIndex : function(){
17441         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17442     },
17443
17444     getShim : function(){
17445         if(!this.useShim){
17446             return null;
17447         }
17448         if(this.shim){
17449             return this.shim;
17450         }
17451         var shim = shims.shift();
17452         if(!shim){
17453             shim = this.createShim();
17454             shim.enableDisplayMode('block');
17455             shim.dom.style.display = 'none';
17456             shim.dom.style.visibility = 'visible';
17457         }
17458         var pn = this.dom.parentNode;
17459         if(shim.dom.parentNode != pn){
17460             pn.insertBefore(shim.dom, this.dom);
17461         }
17462         shim.setStyle('z-index', this.getZIndex()-2);
17463         this.shim = shim;
17464         return shim;
17465     },
17466
17467     hideShim : function(){
17468         if(this.shim){
17469             this.shim.setDisplayed(false);
17470             shims.push(this.shim);
17471             delete this.shim;
17472         }
17473     },
17474
17475     disableShadow : function(){
17476         if(this.shadow){
17477             this.shadowDisabled = true;
17478             this.shadow.hide();
17479             this.lastShadowOffset = this.shadowOffset;
17480             this.shadowOffset = 0;
17481         }
17482     },
17483
17484     enableShadow : function(show){
17485         if(this.shadow){
17486             this.shadowDisabled = false;
17487             this.shadowOffset = this.lastShadowOffset;
17488             delete this.lastShadowOffset;
17489             if(show){
17490                 this.sync(true);
17491             }
17492         }
17493     },
17494
17495     // private
17496     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17497     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17498     sync : function(doShow){
17499         var sw = this.shadow;
17500         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17501             var sh = this.getShim();
17502
17503             var w = this.getWidth(),
17504                 h = this.getHeight();
17505
17506             var l = this.getLeft(true),
17507                 t = this.getTop(true);
17508
17509             if(sw && !this.shadowDisabled){
17510                 if(doShow && !sw.isVisible()){
17511                     sw.show(this);
17512                 }else{
17513                     sw.realign(l, t, w, h);
17514                 }
17515                 if(sh){
17516                     if(doShow){
17517                        sh.show();
17518                     }
17519                     // fit the shim behind the shadow, so it is shimmed too
17520                     var a = sw.adjusts, s = sh.dom.style;
17521                     s.left = (Math.min(l, l+a.l))+"px";
17522                     s.top = (Math.min(t, t+a.t))+"px";
17523                     s.width = (w+a.w)+"px";
17524                     s.height = (h+a.h)+"px";
17525                 }
17526             }else if(sh){
17527                 if(doShow){
17528                    sh.show();
17529                 }
17530                 sh.setSize(w, h);
17531                 sh.setLeftTop(l, t);
17532             }
17533             
17534         }
17535     },
17536
17537     // private
17538     destroy : function(){
17539         this.hideShim();
17540         if(this.shadow){
17541             this.shadow.hide();
17542         }
17543         this.removeAllListeners();
17544         var pn = this.dom.parentNode;
17545         if(pn){
17546             pn.removeChild(this.dom);
17547         }
17548         Roo.Element.uncache(this.id);
17549     },
17550
17551     remove : function(){
17552         this.destroy();
17553     },
17554
17555     // private
17556     beginUpdate : function(){
17557         this.updating = true;
17558     },
17559
17560     // private
17561     endUpdate : function(){
17562         this.updating = false;
17563         this.sync(true);
17564     },
17565
17566     // private
17567     hideUnders : function(negOffset){
17568         if(this.shadow){
17569             this.shadow.hide();
17570         }
17571         this.hideShim();
17572     },
17573
17574     // private
17575     constrainXY : function(){
17576         if(this.constrain){
17577             var vw = Roo.lib.Dom.getViewWidth(),
17578                 vh = Roo.lib.Dom.getViewHeight();
17579             var s = Roo.get(document).getScroll();
17580
17581             var xy = this.getXY();
17582             var x = xy[0], y = xy[1];   
17583             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17584             // only move it if it needs it
17585             var moved = false;
17586             // first validate right/bottom
17587             if((x + w) > vw+s.left){
17588                 x = vw - w - this.shadowOffset;
17589                 moved = true;
17590             }
17591             if((y + h) > vh+s.top){
17592                 y = vh - h - this.shadowOffset;
17593                 moved = true;
17594             }
17595             // then make sure top/left isn't negative
17596             if(x < s.left){
17597                 x = s.left;
17598                 moved = true;
17599             }
17600             if(y < s.top){
17601                 y = s.top;
17602                 moved = true;
17603             }
17604             if(moved){
17605                 if(this.avoidY){
17606                     var ay = this.avoidY;
17607                     if(y <= ay && (y+h) >= ay){
17608                         y = ay-h-5;   
17609                     }
17610                 }
17611                 xy = [x, y];
17612                 this.storeXY(xy);
17613                 supr.setXY.call(this, xy);
17614                 this.sync();
17615             }
17616         }
17617     },
17618
17619     isVisible : function(){
17620         return this.visible;    
17621     },
17622
17623     // private
17624     showAction : function(){
17625         this.visible = true; // track visibility to prevent getStyle calls
17626         if(this.useDisplay === true){
17627             this.setDisplayed("");
17628         }else if(this.lastXY){
17629             supr.setXY.call(this, this.lastXY);
17630         }else if(this.lastLT){
17631             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17632         }
17633     },
17634
17635     // private
17636     hideAction : function(){
17637         this.visible = false;
17638         if(this.useDisplay === true){
17639             this.setDisplayed(false);
17640         }else{
17641             this.setLeftTop(-10000,-10000);
17642         }
17643     },
17644
17645     // overridden Element method
17646     setVisible : function(v, a, d, c, e){
17647         if(v){
17648             this.showAction();
17649         }
17650         if(a && v){
17651             var cb = function(){
17652                 this.sync(true);
17653                 if(c){
17654                     c();
17655                 }
17656             }.createDelegate(this);
17657             supr.setVisible.call(this, true, true, d, cb, e);
17658         }else{
17659             if(!v){
17660                 this.hideUnders(true);
17661             }
17662             var cb = c;
17663             if(a){
17664                 cb = function(){
17665                     this.hideAction();
17666                     if(c){
17667                         c();
17668                     }
17669                 }.createDelegate(this);
17670             }
17671             supr.setVisible.call(this, v, a, d, cb, e);
17672             if(v){
17673                 this.sync(true);
17674             }else if(!a){
17675                 this.hideAction();
17676             }
17677         }
17678     },
17679
17680     storeXY : function(xy){
17681         delete this.lastLT;
17682         this.lastXY = xy;
17683     },
17684
17685     storeLeftTop : function(left, top){
17686         delete this.lastXY;
17687         this.lastLT = [left, top];
17688     },
17689
17690     // private
17691     beforeFx : function(){
17692         this.beforeAction();
17693         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17694     },
17695
17696     // private
17697     afterFx : function(){
17698         Roo.Layer.superclass.afterFx.apply(this, arguments);
17699         this.sync(this.isVisible());
17700     },
17701
17702     // private
17703     beforeAction : function(){
17704         if(!this.updating && this.shadow){
17705             this.shadow.hide();
17706         }
17707     },
17708
17709     // overridden Element method
17710     setLeft : function(left){
17711         this.storeLeftTop(left, this.getTop(true));
17712         supr.setLeft.apply(this, arguments);
17713         this.sync();
17714     },
17715
17716     setTop : function(top){
17717         this.storeLeftTop(this.getLeft(true), top);
17718         supr.setTop.apply(this, arguments);
17719         this.sync();
17720     },
17721
17722     setLeftTop : function(left, top){
17723         this.storeLeftTop(left, top);
17724         supr.setLeftTop.apply(this, arguments);
17725         this.sync();
17726     },
17727
17728     setXY : function(xy, a, d, c, e){
17729         this.fixDisplay();
17730         this.beforeAction();
17731         this.storeXY(xy);
17732         var cb = this.createCB(c);
17733         supr.setXY.call(this, xy, a, d, cb, e);
17734         if(!a){
17735             cb();
17736         }
17737     },
17738
17739     // private
17740     createCB : function(c){
17741         var el = this;
17742         return function(){
17743             el.constrainXY();
17744             el.sync(true);
17745             if(c){
17746                 c();
17747             }
17748         };
17749     },
17750
17751     // overridden Element method
17752     setX : function(x, a, d, c, e){
17753         this.setXY([x, this.getY()], a, d, c, e);
17754     },
17755
17756     // overridden Element method
17757     setY : function(y, a, d, c, e){
17758         this.setXY([this.getX(), y], a, d, c, e);
17759     },
17760
17761     // overridden Element method
17762     setSize : function(w, h, a, d, c, e){
17763         this.beforeAction();
17764         var cb = this.createCB(c);
17765         supr.setSize.call(this, w, h, a, d, cb, e);
17766         if(!a){
17767             cb();
17768         }
17769     },
17770
17771     // overridden Element method
17772     setWidth : function(w, a, d, c, e){
17773         this.beforeAction();
17774         var cb = this.createCB(c);
17775         supr.setWidth.call(this, w, a, d, cb, e);
17776         if(!a){
17777             cb();
17778         }
17779     },
17780
17781     // overridden Element method
17782     setHeight : function(h, a, d, c, e){
17783         this.beforeAction();
17784         var cb = this.createCB(c);
17785         supr.setHeight.call(this, h, a, d, cb, e);
17786         if(!a){
17787             cb();
17788         }
17789     },
17790
17791     // overridden Element method
17792     setBounds : function(x, y, w, h, a, d, c, e){
17793         this.beforeAction();
17794         var cb = this.createCB(c);
17795         if(!a){
17796             this.storeXY([x, y]);
17797             supr.setXY.call(this, [x, y]);
17798             supr.setSize.call(this, w, h, a, d, cb, e);
17799             cb();
17800         }else{
17801             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17802         }
17803         return this;
17804     },
17805     
17806     /**
17807      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17808      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17809      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17810      * @param {Number} zindex The new z-index to set
17811      * @return {this} The Layer
17812      */
17813     setZIndex : function(zindex){
17814         this.zindex = zindex;
17815         this.setStyle("z-index", zindex + 2);
17816         if(this.shadow){
17817             this.shadow.setZIndex(zindex + 1);
17818         }
17819         if(this.shim){
17820             this.shim.setStyle("z-index", zindex);
17821         }
17822     }
17823 });
17824 })();/*
17825  * Original code for Roojs - LGPL
17826  * <script type="text/javascript">
17827  */
17828  
17829 /**
17830  * @class Roo.XComponent
17831  * A delayed Element creator...
17832  * Or a way to group chunks of interface together.
17833  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17834  *  used in conjunction with XComponent.build() it will create an instance of each element,
17835  *  then call addxtype() to build the User interface.
17836  * 
17837  * Mypart.xyx = new Roo.XComponent({
17838
17839     parent : 'Mypart.xyz', // empty == document.element.!!
17840     order : '001',
17841     name : 'xxxx'
17842     region : 'xxxx'
17843     disabled : function() {} 
17844      
17845     tree : function() { // return an tree of xtype declared components
17846         var MODULE = this;
17847         return 
17848         {
17849             xtype : 'NestedLayoutPanel',
17850             // technicall
17851         }
17852      ]
17853  *})
17854  *
17855  *
17856  * It can be used to build a big heiracy, with parent etc.
17857  * or you can just use this to render a single compoent to a dom element
17858  * MYPART.render(Roo.Element | String(id) | dom_element )
17859  *
17860  *
17861  * Usage patterns.
17862  *
17863  * Classic Roo
17864  *
17865  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17866  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17867  *
17868  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17869  *
17870  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17871  * - if mulitple topModules exist, the last one is defined as the top module.
17872  *
17873  * Embeded Roo
17874  * 
17875  * When the top level or multiple modules are to embedded into a existing HTML page,
17876  * the parent element can container '#id' of the element where the module will be drawn.
17877  *
17878  * Bootstrap Roo
17879  *
17880  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17881  * it relies more on a include mechanism, where sub modules are included into an outer page.
17882  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17883  * 
17884  * Bootstrap Roo Included elements
17885  *
17886  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17887  * hence confusing the component builder as it thinks there are multiple top level elements. 
17888  *
17889  * String Over-ride & Translations
17890  *
17891  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17892  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17893  * are needed. @see Roo.XComponent.overlayString  
17894  * 
17895  * 
17896  * 
17897  * @extends Roo.util.Observable
17898  * @constructor
17899  * @param cfg {Object} configuration of component
17900  * 
17901  */
17902 Roo.XComponent = function(cfg) {
17903     Roo.apply(this, cfg);
17904     this.addEvents({ 
17905         /**
17906              * @event built
17907              * Fires when this the componnt is built
17908              * @param {Roo.XComponent} c the component
17909              */
17910         'built' : true
17911         
17912     });
17913     this.region = this.region || 'center'; // default..
17914     Roo.XComponent.register(this);
17915     this.modules = false;
17916     this.el = false; // where the layout goes..
17917     
17918     
17919 }
17920 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17921     /**
17922      * @property el
17923      * The created element (with Roo.factory())
17924      * @type {Roo.Layout}
17925      */
17926     el  : false,
17927     
17928     /**
17929      * @property el
17930      * for BC  - use el in new code
17931      * @type {Roo.Layout}
17932      */
17933     panel : false,
17934     
17935     /**
17936      * @property layout
17937      * for BC  - use el in new code
17938      * @type {Roo.Layout}
17939      */
17940     layout : false,
17941     
17942      /**
17943      * @cfg {Function|boolean} disabled
17944      * If this module is disabled by some rule, return true from the funtion
17945      */
17946     disabled : false,
17947     
17948     /**
17949      * @cfg {String} parent 
17950      * Name of parent element which it get xtype added to..
17951      */
17952     parent: false,
17953     
17954     /**
17955      * @cfg {String} order
17956      * Used to set the order in which elements are created (usefull for multiple tabs)
17957      */
17958     
17959     order : false,
17960     /**
17961      * @cfg {String} name
17962      * String to display while loading.
17963      */
17964     name : false,
17965     /**
17966      * @cfg {String} region
17967      * Region to render component to (defaults to center)
17968      */
17969     region : 'center',
17970     
17971     /**
17972      * @cfg {Array} items
17973      * A single item array - the first element is the root of the tree..
17974      * It's done this way to stay compatible with the Xtype system...
17975      */
17976     items : false,
17977     
17978     /**
17979      * @property _tree
17980      * The method that retuns the tree of parts that make up this compoennt 
17981      * @type {function}
17982      */
17983     _tree  : false,
17984     
17985      /**
17986      * render
17987      * render element to dom or tree
17988      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17989      */
17990     
17991     render : function(el)
17992     {
17993         
17994         el = el || false;
17995         var hp = this.parent ? 1 : 0;
17996         Roo.debug &&  Roo.log(this);
17997         
17998         var tree = this._tree ? this._tree() : this.tree();
17999
18000         
18001         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
18002             // if parent is a '#.....' string, then let's use that..
18003             var ename = this.parent.substr(1);
18004             this.parent = false;
18005             Roo.debug && Roo.log(ename);
18006             switch (ename) {
18007                 case 'bootstrap-body':
18008                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
18009                         // this is the BorderLayout standard?
18010                        this.parent = { el : true };
18011                        break;
18012                     }
18013                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
18014                         // need to insert stuff...
18015                         this.parent =  {
18016                              el : new Roo.bootstrap.layout.Border({
18017                                  el : document.body, 
18018                      
18019                                  center: {
18020                                     titlebar: false,
18021                                     autoScroll:false,
18022                                     closeOnTab: true,
18023                                     tabPosition: 'top',
18024                                       //resizeTabs: true,
18025                                     alwaysShowTabs: true,
18026                                     hideTabs: false
18027                                      //minTabWidth: 140
18028                                  }
18029                              })
18030                         
18031                          };
18032                          break;
18033                     }
18034                          
18035                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18036                         this.parent = { el :  new  Roo.bootstrap.Body() };
18037                         Roo.debug && Roo.log("setting el to doc body");
18038                          
18039                     } else {
18040                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18041                     }
18042                     break;
18043                 case 'bootstrap':
18044                     this.parent = { el : true};
18045                     // fall through
18046                 default:
18047                     el = Roo.get(ename);
18048                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18049                         this.parent = { el : true};
18050                     }
18051                     
18052                     break;
18053             }
18054                 
18055             
18056             if (!el && !this.parent) {
18057                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18058                 return;
18059             }
18060         }
18061         
18062         Roo.debug && Roo.log("EL:");
18063         Roo.debug && Roo.log(el);
18064         Roo.debug && Roo.log("this.parent.el:");
18065         Roo.debug && Roo.log(this.parent.el);
18066         
18067
18068         // altertive root elements ??? - we need a better way to indicate these.
18069         var is_alt = Roo.XComponent.is_alt ||
18070                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18071                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18072                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18073         
18074         
18075         
18076         if (!this.parent && is_alt) {
18077             //el = Roo.get(document.body);
18078             this.parent = { el : true };
18079         }
18080             
18081             
18082         
18083         if (!this.parent) {
18084             
18085             Roo.debug && Roo.log("no parent - creating one");
18086             
18087             el = el ? Roo.get(el) : false;      
18088             
18089             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18090                 
18091                 this.parent =  {
18092                     el : new Roo.bootstrap.layout.Border({
18093                         el: el || document.body,
18094                     
18095                         center: {
18096                             titlebar: false,
18097                             autoScroll:false,
18098                             closeOnTab: true,
18099                             tabPosition: 'top',
18100                              //resizeTabs: true,
18101                             alwaysShowTabs: false,
18102                             hideTabs: true,
18103                             minTabWidth: 140,
18104                             overflow: 'visible'
18105                          }
18106                      })
18107                 };
18108             } else {
18109             
18110                 // it's a top level one..
18111                 this.parent =  {
18112                     el : new Roo.BorderLayout(el || document.body, {
18113                         center: {
18114                             titlebar: false,
18115                             autoScroll:false,
18116                             closeOnTab: true,
18117                             tabPosition: 'top',
18118                              //resizeTabs: true,
18119                             alwaysShowTabs: el && hp? false :  true,
18120                             hideTabs: el || !hp ? true :  false,
18121                             minTabWidth: 140
18122                          }
18123                     })
18124                 };
18125             }
18126         }
18127         
18128         if (!this.parent.el) {
18129                 // probably an old style ctor, which has been disabled.
18130                 return;
18131
18132         }
18133                 // The 'tree' method is  '_tree now' 
18134             
18135         tree.region = tree.region || this.region;
18136         var is_body = false;
18137         if (this.parent.el === true) {
18138             // bootstrap... - body..
18139             if (el) {
18140                 tree.el = el;
18141             }
18142             this.parent.el = Roo.factory(tree);
18143             is_body = true;
18144         }
18145         
18146         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18147         this.fireEvent('built', this);
18148         
18149         this.panel = this.el;
18150         this.layout = this.panel.layout;
18151         this.parentLayout = this.parent.layout  || false;  
18152          
18153     }
18154     
18155 });
18156
18157 Roo.apply(Roo.XComponent, {
18158     /**
18159      * @property  hideProgress
18160      * true to disable the building progress bar.. usefull on single page renders.
18161      * @type Boolean
18162      */
18163     hideProgress : false,
18164     /**
18165      * @property  buildCompleted
18166      * True when the builder has completed building the interface.
18167      * @type Boolean
18168      */
18169     buildCompleted : false,
18170      
18171     /**
18172      * @property  topModule
18173      * the upper most module - uses document.element as it's constructor.
18174      * @type Object
18175      */
18176      
18177     topModule  : false,
18178       
18179     /**
18180      * @property  modules
18181      * array of modules to be created by registration system.
18182      * @type {Array} of Roo.XComponent
18183      */
18184     
18185     modules : [],
18186     /**
18187      * @property  elmodules
18188      * array of modules to be created by which use #ID 
18189      * @type {Array} of Roo.XComponent
18190      */
18191      
18192     elmodules : [],
18193
18194      /**
18195      * @property  is_alt
18196      * Is an alternative Root - normally used by bootstrap or other systems,
18197      *    where the top element in the tree can wrap 'body' 
18198      * @type {boolean}  (default false)
18199      */
18200      
18201     is_alt : false,
18202     /**
18203      * @property  build_from_html
18204      * Build elements from html - used by bootstrap HTML stuff 
18205      *    - this is cleared after build is completed
18206      * @type {boolean}    (default false)
18207      */
18208      
18209     build_from_html : false,
18210     /**
18211      * Register components to be built later.
18212      *
18213      * This solves the following issues
18214      * - Building is not done on page load, but after an authentication process has occured.
18215      * - Interface elements are registered on page load
18216      * - Parent Interface elements may not be loaded before child, so this handles that..
18217      * 
18218      *
18219      * example:
18220      * 
18221      * MyApp.register({
18222           order : '000001',
18223           module : 'Pman.Tab.projectMgr',
18224           region : 'center',
18225           parent : 'Pman.layout',
18226           disabled : false,  // or use a function..
18227         })
18228      
18229      * * @param {Object} details about module
18230      */
18231     register : function(obj) {
18232                 
18233         Roo.XComponent.event.fireEvent('register', obj);
18234         switch(typeof(obj.disabled) ) {
18235                 
18236             case 'undefined':
18237                 break;
18238             
18239             case 'function':
18240                 if ( obj.disabled() ) {
18241                         return;
18242                 }
18243                 break;
18244             
18245             default:
18246                 if (obj.disabled || obj.region == '#disabled') {
18247                         return;
18248                 }
18249                 break;
18250         }
18251                 
18252         this.modules.push(obj);
18253          
18254     },
18255     /**
18256      * convert a string to an object..
18257      * eg. 'AAA.BBB' -> finds AAA.BBB
18258
18259      */
18260     
18261     toObject : function(str)
18262     {
18263         if (!str || typeof(str) == 'object') {
18264             return str;
18265         }
18266         if (str.substring(0,1) == '#') {
18267             return str;
18268         }
18269
18270         var ar = str.split('.');
18271         var rt, o;
18272         rt = ar.shift();
18273             /** eval:var:o */
18274         try {
18275             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18276         } catch (e) {
18277             throw "Module not found : " + str;
18278         }
18279         
18280         if (o === false) {
18281             throw "Module not found : " + str;
18282         }
18283         Roo.each(ar, function(e) {
18284             if (typeof(o[e]) == 'undefined') {
18285                 throw "Module not found : " + str;
18286             }
18287             o = o[e];
18288         });
18289         
18290         return o;
18291         
18292     },
18293     
18294     
18295     /**
18296      * move modules into their correct place in the tree..
18297      * 
18298      */
18299     preBuild : function ()
18300     {
18301         var _t = this;
18302         Roo.each(this.modules , function (obj)
18303         {
18304             Roo.XComponent.event.fireEvent('beforebuild', obj);
18305             
18306             var opar = obj.parent;
18307             try { 
18308                 obj.parent = this.toObject(opar);
18309             } catch(e) {
18310                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18311                 return;
18312             }
18313             
18314             if (!obj.parent) {
18315                 Roo.debug && Roo.log("GOT top level module");
18316                 Roo.debug && Roo.log(obj);
18317                 obj.modules = new Roo.util.MixedCollection(false, 
18318                     function(o) { return o.order + '' }
18319                 );
18320                 this.topModule = obj;
18321                 return;
18322             }
18323                         // parent is a string (usually a dom element name..)
18324             if (typeof(obj.parent) == 'string') {
18325                 this.elmodules.push(obj);
18326                 return;
18327             }
18328             if (obj.parent.constructor != Roo.XComponent) {
18329                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18330             }
18331             if (!obj.parent.modules) {
18332                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18333                     function(o) { return o.order + '' }
18334                 );
18335             }
18336             if (obj.parent.disabled) {
18337                 obj.disabled = true;
18338             }
18339             obj.parent.modules.add(obj);
18340         }, this);
18341     },
18342     
18343      /**
18344      * make a list of modules to build.
18345      * @return {Array} list of modules. 
18346      */ 
18347     
18348     buildOrder : function()
18349     {
18350         var _this = this;
18351         var cmp = function(a,b) {   
18352             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18353         };
18354         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18355             throw "No top level modules to build";
18356         }
18357         
18358         // make a flat list in order of modules to build.
18359         var mods = this.topModule ? [ this.topModule ] : [];
18360                 
18361         
18362         // elmodules (is a list of DOM based modules )
18363         Roo.each(this.elmodules, function(e) {
18364             mods.push(e);
18365             if (!this.topModule &&
18366                 typeof(e.parent) == 'string' &&
18367                 e.parent.substring(0,1) == '#' &&
18368                 Roo.get(e.parent.substr(1))
18369                ) {
18370                 
18371                 _this.topModule = e;
18372             }
18373             
18374         });
18375
18376         
18377         // add modules to their parents..
18378         var addMod = function(m) {
18379             Roo.debug && Roo.log("build Order: add: " + m.name);
18380                 
18381             mods.push(m);
18382             if (m.modules && !m.disabled) {
18383                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18384                 m.modules.keySort('ASC',  cmp );
18385                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18386     
18387                 m.modules.each(addMod);
18388             } else {
18389                 Roo.debug && Roo.log("build Order: no child modules");
18390             }
18391             // not sure if this is used any more..
18392             if (m.finalize) {
18393                 m.finalize.name = m.name + " (clean up) ";
18394                 mods.push(m.finalize);
18395             }
18396             
18397         }
18398         if (this.topModule && this.topModule.modules) { 
18399             this.topModule.modules.keySort('ASC',  cmp );
18400             this.topModule.modules.each(addMod);
18401         } 
18402         return mods;
18403     },
18404     
18405      /**
18406      * Build the registered modules.
18407      * @param {Object} parent element.
18408      * @param {Function} optional method to call after module has been added.
18409      * 
18410      */ 
18411    
18412     build : function(opts) 
18413     {
18414         
18415         if (typeof(opts) != 'undefined') {
18416             Roo.apply(this,opts);
18417         }
18418         
18419         this.preBuild();
18420         var mods = this.buildOrder();
18421       
18422         //this.allmods = mods;
18423         //Roo.debug && Roo.log(mods);
18424         //return;
18425         if (!mods.length) { // should not happen
18426             throw "NO modules!!!";
18427         }
18428         
18429         
18430         var msg = "Building Interface...";
18431         // flash it up as modal - so we store the mask!?
18432         if (!this.hideProgress && Roo.MessageBox) {
18433             Roo.MessageBox.show({ title: 'loading' });
18434             Roo.MessageBox.show({
18435                title: "Please wait...",
18436                msg: msg,
18437                width:450,
18438                progress:true,
18439                buttons : false,
18440                closable:false,
18441                modal: false
18442               
18443             });
18444         }
18445         var total = mods.length;
18446         
18447         var _this = this;
18448         var progressRun = function() {
18449             if (!mods.length) {
18450                 Roo.debug && Roo.log('hide?');
18451                 if (!this.hideProgress && Roo.MessageBox) {
18452                     Roo.MessageBox.hide();
18453                 }
18454                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18455                 
18456                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18457                 
18458                 // THE END...
18459                 return false;   
18460             }
18461             
18462             var m = mods.shift();
18463             
18464             
18465             Roo.debug && Roo.log(m);
18466             // not sure if this is supported any more.. - modules that are are just function
18467             if (typeof(m) == 'function') { 
18468                 m.call(this);
18469                 return progressRun.defer(10, _this);
18470             } 
18471             
18472             
18473             msg = "Building Interface " + (total  - mods.length) + 
18474                     " of " + total + 
18475                     (m.name ? (' - ' + m.name) : '');
18476                         Roo.debug && Roo.log(msg);
18477             if (!_this.hideProgress &&  Roo.MessageBox) { 
18478                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18479             }
18480             
18481          
18482             // is the module disabled?
18483             var disabled = (typeof(m.disabled) == 'function') ?
18484                 m.disabled.call(m.module.disabled) : m.disabled;    
18485             
18486             
18487             if (disabled) {
18488                 return progressRun(); // we do not update the display!
18489             }
18490             
18491             // now build 
18492             
18493                         
18494                         
18495             m.render();
18496             // it's 10 on top level, and 1 on others??? why...
18497             return progressRun.defer(10, _this);
18498              
18499         }
18500         progressRun.defer(1, _this);
18501      
18502         
18503         
18504     },
18505     /**
18506      * Overlay a set of modified strings onto a component
18507      * This is dependant on our builder exporting the strings and 'named strings' elements.
18508      * 
18509      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18510      * @param {Object} associative array of 'named' string and it's new value.
18511      * 
18512      */
18513         overlayStrings : function( component, strings )
18514     {
18515         if (typeof(component['_named_strings']) == 'undefined') {
18516             throw "ERROR: component does not have _named_strings";
18517         }
18518         for ( var k in strings ) {
18519             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18520             if (md !== false) {
18521                 component['_strings'][md] = strings[k];
18522             } else {
18523                 Roo.log('could not find named string: ' + k + ' in');
18524                 Roo.log(component);
18525             }
18526             
18527         }
18528         
18529     },
18530     
18531         
18532         /**
18533          * Event Object.
18534          *
18535          *
18536          */
18537         event: false, 
18538     /**
18539          * wrapper for event.on - aliased later..  
18540          * Typically use to register a event handler for register:
18541          *
18542          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18543          *
18544          */
18545     on : false
18546    
18547     
18548     
18549 });
18550
18551 Roo.XComponent.event = new Roo.util.Observable({
18552                 events : { 
18553                         /**
18554                          * @event register
18555                          * Fires when an Component is registered,
18556                          * set the disable property on the Component to stop registration.
18557                          * @param {Roo.XComponent} c the component being registerd.
18558                          * 
18559                          */
18560                         'register' : true,
18561             /**
18562                          * @event beforebuild
18563                          * Fires before each Component is built
18564                          * can be used to apply permissions.
18565                          * @param {Roo.XComponent} c the component being registerd.
18566                          * 
18567                          */
18568                         'beforebuild' : true,
18569                         /**
18570                          * @event buildcomplete
18571                          * Fires on the top level element when all elements have been built
18572                          * @param {Roo.XComponent} the top level component.
18573                          */
18574                         'buildcomplete' : true
18575                         
18576                 }
18577 });
18578
18579 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18580  //
18581  /**
18582  * marked - a markdown parser
18583  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18584  * https://github.com/chjj/marked
18585  */
18586
18587
18588 /**
18589  *
18590  * Roo.Markdown - is a very crude wrapper around marked..
18591  *
18592  * usage:
18593  * 
18594  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18595  * 
18596  * Note: move the sample code to the bottom of this
18597  * file before uncommenting it.
18598  *
18599  */
18600
18601 Roo.Markdown = {};
18602 Roo.Markdown.toHtml = function(text) {
18603     
18604     var c = new Roo.Markdown.marked.setOptions({
18605             renderer: new Roo.Markdown.marked.Renderer(),
18606             gfm: true,
18607             tables: true,
18608             breaks: false,
18609             pedantic: false,
18610             sanitize: false,
18611             smartLists: true,
18612             smartypants: false
18613           });
18614     // A FEW HACKS!!?
18615     
18616     text = text.replace(/\\\n/g,' ');
18617     return Roo.Markdown.marked(text);
18618 };
18619 //
18620 // converter
18621 //
18622 // Wraps all "globals" so that the only thing
18623 // exposed is makeHtml().
18624 //
18625 (function() {
18626     
18627      /**
18628          * eval:var:escape
18629          * eval:var:unescape
18630          * eval:var:replace
18631          */
18632       
18633     /**
18634      * Helpers
18635      */
18636     
18637     var escape = function (html, encode) {
18638       return html
18639         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18640         .replace(/</g, '&lt;')
18641         .replace(/>/g, '&gt;')
18642         .replace(/"/g, '&quot;')
18643         .replace(/'/g, '&#39;');
18644     }
18645     
18646     var unescape = function (html) {
18647         // explicitly match decimal, hex, and named HTML entities 
18648       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18649         n = n.toLowerCase();
18650         if (n === 'colon') { return ':'; }
18651         if (n.charAt(0) === '#') {
18652           return n.charAt(1) === 'x'
18653             ? String.fromCharCode(parseInt(n.substring(2), 16))
18654             : String.fromCharCode(+n.substring(1));
18655         }
18656         return '';
18657       });
18658     }
18659     
18660     var replace = function (regex, opt) {
18661       regex = regex.source;
18662       opt = opt || '';
18663       return function self(name, val) {
18664         if (!name) { return new RegExp(regex, opt); }
18665         val = val.source || val;
18666         val = val.replace(/(^|[^\[])\^/g, '$1');
18667         regex = regex.replace(name, val);
18668         return self;
18669       };
18670     }
18671
18672
18673          /**
18674          * eval:var:noop
18675     */
18676     var noop = function () {}
18677     noop.exec = noop;
18678     
18679          /**
18680          * eval:var:merge
18681     */
18682     var merge = function (obj) {
18683       var i = 1
18684         , target
18685         , key;
18686     
18687       for (; i < arguments.length; i++) {
18688         target = arguments[i];
18689         for (key in target) {
18690           if (Object.prototype.hasOwnProperty.call(target, key)) {
18691             obj[key] = target[key];
18692           }
18693         }
18694       }
18695     
18696       return obj;
18697     }
18698     
18699     
18700     /**
18701      * Block-Level Grammar
18702      */
18703     
18704     
18705     
18706     
18707     var block = {
18708       newline: /^\n+/,
18709       code: /^( {4}[^\n]+\n*)+/,
18710       fences: noop,
18711       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18712       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18713       nptable: noop,
18714       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18715       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18716       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18717       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18718       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18719       table: noop,
18720       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18721       text: /^[^\n]+/
18722     };
18723     
18724     block.bullet = /(?:[*+-]|\d+\.)/;
18725     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18726     block.item = replace(block.item, 'gm')
18727       (/bull/g, block.bullet)
18728       ();
18729     
18730     block.list = replace(block.list)
18731       (/bull/g, block.bullet)
18732       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18733       ('def', '\\n+(?=' + block.def.source + ')')
18734       ();
18735     
18736     block.blockquote = replace(block.blockquote)
18737       ('def', block.def)
18738       ();
18739     
18740     block._tag = '(?!(?:'
18741       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18742       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18743       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18744     
18745     block.html = replace(block.html)
18746       ('comment', /<!--[\s\S]*?-->/)
18747       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18748       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18749       (/tag/g, block._tag)
18750       ();
18751     
18752     block.paragraph = replace(block.paragraph)
18753       ('hr', block.hr)
18754       ('heading', block.heading)
18755       ('lheading', block.lheading)
18756       ('blockquote', block.blockquote)
18757       ('tag', '<' + block._tag)
18758       ('def', block.def)
18759       ();
18760     
18761     /**
18762      * Normal Block Grammar
18763      */
18764     
18765     block.normal = merge({}, block);
18766     
18767     /**
18768      * GFM Block Grammar
18769      */
18770     
18771     block.gfm = merge({}, block.normal, {
18772       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18773       paragraph: /^/,
18774       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18775     });
18776     
18777     block.gfm.paragraph = replace(block.paragraph)
18778       ('(?!', '(?!'
18779         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18780         + block.list.source.replace('\\1', '\\3') + '|')
18781       ();
18782     
18783     /**
18784      * GFM + Tables Block Grammar
18785      */
18786     
18787     block.tables = merge({}, block.gfm, {
18788       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18789       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18790     });
18791     
18792     /**
18793      * Block Lexer
18794      */
18795     
18796     var Lexer = function (options) {
18797       this.tokens = [];
18798       this.tokens.links = {};
18799       this.options = options || marked.defaults;
18800       this.rules = block.normal;
18801     
18802       if (this.options.gfm) {
18803         if (this.options.tables) {
18804           this.rules = block.tables;
18805         } else {
18806           this.rules = block.gfm;
18807         }
18808       }
18809     }
18810     
18811     /**
18812      * Expose Block Rules
18813      */
18814     
18815     Lexer.rules = block;
18816     
18817     /**
18818      * Static Lex Method
18819      */
18820     
18821     Lexer.lex = function(src, options) {
18822       var lexer = new Lexer(options);
18823       return lexer.lex(src);
18824     };
18825     
18826     /**
18827      * Preprocessing
18828      */
18829     
18830     Lexer.prototype.lex = function(src) {
18831       src = src
18832         .replace(/\r\n|\r/g, '\n')
18833         .replace(/\t/g, '    ')
18834         .replace(/\u00a0/g, ' ')
18835         .replace(/\u2424/g, '\n');
18836     
18837       return this.token(src, true);
18838     };
18839     
18840     /**
18841      * Lexing
18842      */
18843     
18844     Lexer.prototype.token = function(src, top, bq) {
18845       var src = src.replace(/^ +$/gm, '')
18846         , next
18847         , loose
18848         , cap
18849         , bull
18850         , b
18851         , item
18852         , space
18853         , i
18854         , l;
18855     
18856       while (src) {
18857         // newline
18858         if (cap = this.rules.newline.exec(src)) {
18859           src = src.substring(cap[0].length);
18860           if (cap[0].length > 1) {
18861             this.tokens.push({
18862               type: 'space'
18863             });
18864           }
18865         }
18866     
18867         // code
18868         if (cap = this.rules.code.exec(src)) {
18869           src = src.substring(cap[0].length);
18870           cap = cap[0].replace(/^ {4}/gm, '');
18871           this.tokens.push({
18872             type: 'code',
18873             text: !this.options.pedantic
18874               ? cap.replace(/\n+$/, '')
18875               : cap
18876           });
18877           continue;
18878         }
18879     
18880         // fences (gfm)
18881         if (cap = this.rules.fences.exec(src)) {
18882           src = src.substring(cap[0].length);
18883           this.tokens.push({
18884             type: 'code',
18885             lang: cap[2],
18886             text: cap[3] || ''
18887           });
18888           continue;
18889         }
18890     
18891         // heading
18892         if (cap = this.rules.heading.exec(src)) {
18893           src = src.substring(cap[0].length);
18894           this.tokens.push({
18895             type: 'heading',
18896             depth: cap[1].length,
18897             text: cap[2]
18898           });
18899           continue;
18900         }
18901     
18902         // table no leading pipe (gfm)
18903         if (top && (cap = this.rules.nptable.exec(src))) {
18904           src = src.substring(cap[0].length);
18905     
18906           item = {
18907             type: 'table',
18908             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18909             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18910             cells: cap[3].replace(/\n$/, '').split('\n')
18911           };
18912     
18913           for (i = 0; i < item.align.length; i++) {
18914             if (/^ *-+: *$/.test(item.align[i])) {
18915               item.align[i] = 'right';
18916             } else if (/^ *:-+: *$/.test(item.align[i])) {
18917               item.align[i] = 'center';
18918             } else if (/^ *:-+ *$/.test(item.align[i])) {
18919               item.align[i] = 'left';
18920             } else {
18921               item.align[i] = null;
18922             }
18923           }
18924     
18925           for (i = 0; i < item.cells.length; i++) {
18926             item.cells[i] = item.cells[i].split(/ *\| */);
18927           }
18928     
18929           this.tokens.push(item);
18930     
18931           continue;
18932         }
18933     
18934         // lheading
18935         if (cap = this.rules.lheading.exec(src)) {
18936           src = src.substring(cap[0].length);
18937           this.tokens.push({
18938             type: 'heading',
18939             depth: cap[2] === '=' ? 1 : 2,
18940             text: cap[1]
18941           });
18942           continue;
18943         }
18944     
18945         // hr
18946         if (cap = this.rules.hr.exec(src)) {
18947           src = src.substring(cap[0].length);
18948           this.tokens.push({
18949             type: 'hr'
18950           });
18951           continue;
18952         }
18953     
18954         // blockquote
18955         if (cap = this.rules.blockquote.exec(src)) {
18956           src = src.substring(cap[0].length);
18957     
18958           this.tokens.push({
18959             type: 'blockquote_start'
18960           });
18961     
18962           cap = cap[0].replace(/^ *> ?/gm, '');
18963     
18964           // Pass `top` to keep the current
18965           // "toplevel" state. This is exactly
18966           // how markdown.pl works.
18967           this.token(cap, top, true);
18968     
18969           this.tokens.push({
18970             type: 'blockquote_end'
18971           });
18972     
18973           continue;
18974         }
18975     
18976         // list
18977         if (cap = this.rules.list.exec(src)) {
18978           src = src.substring(cap[0].length);
18979           bull = cap[2];
18980     
18981           this.tokens.push({
18982             type: 'list_start',
18983             ordered: bull.length > 1
18984           });
18985     
18986           // Get each top-level item.
18987           cap = cap[0].match(this.rules.item);
18988     
18989           next = false;
18990           l = cap.length;
18991           i = 0;
18992     
18993           for (; i < l; i++) {
18994             item = cap[i];
18995     
18996             // Remove the list item's bullet
18997             // so it is seen as the next token.
18998             space = item.length;
18999             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
19000     
19001             // Outdent whatever the
19002             // list item contains. Hacky.
19003             if (~item.indexOf('\n ')) {
19004               space -= item.length;
19005               item = !this.options.pedantic
19006                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
19007                 : item.replace(/^ {1,4}/gm, '');
19008             }
19009     
19010             // Determine whether the next list item belongs here.
19011             // Backpedal if it does not belong in this list.
19012             if (this.options.smartLists && i !== l - 1) {
19013               b = block.bullet.exec(cap[i + 1])[0];
19014               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
19015                 src = cap.slice(i + 1).join('\n') + src;
19016                 i = l - 1;
19017               }
19018             }
19019     
19020             // Determine whether item is loose or not.
19021             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19022             // for discount behavior.
19023             loose = next || /\n\n(?!\s*$)/.test(item);
19024             if (i !== l - 1) {
19025               next = item.charAt(item.length - 1) === '\n';
19026               if (!loose) { loose = next; }
19027             }
19028     
19029             this.tokens.push({
19030               type: loose
19031                 ? 'loose_item_start'
19032                 : 'list_item_start'
19033             });
19034     
19035             // Recurse.
19036             this.token(item, false, bq);
19037     
19038             this.tokens.push({
19039               type: 'list_item_end'
19040             });
19041           }
19042     
19043           this.tokens.push({
19044             type: 'list_end'
19045           });
19046     
19047           continue;
19048         }
19049     
19050         // html
19051         if (cap = this.rules.html.exec(src)) {
19052           src = src.substring(cap[0].length);
19053           this.tokens.push({
19054             type: this.options.sanitize
19055               ? 'paragraph'
19056               : 'html',
19057             pre: !this.options.sanitizer
19058               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19059             text: cap[0]
19060           });
19061           continue;
19062         }
19063     
19064         // def
19065         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19066           src = src.substring(cap[0].length);
19067           this.tokens.links[cap[1].toLowerCase()] = {
19068             href: cap[2],
19069             title: cap[3]
19070           };
19071           continue;
19072         }
19073     
19074         // table (gfm)
19075         if (top && (cap = this.rules.table.exec(src))) {
19076           src = src.substring(cap[0].length);
19077     
19078           item = {
19079             type: 'table',
19080             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19081             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19082             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19083           };
19084     
19085           for (i = 0; i < item.align.length; i++) {
19086             if (/^ *-+: *$/.test(item.align[i])) {
19087               item.align[i] = 'right';
19088             } else if (/^ *:-+: *$/.test(item.align[i])) {
19089               item.align[i] = 'center';
19090             } else if (/^ *:-+ *$/.test(item.align[i])) {
19091               item.align[i] = 'left';
19092             } else {
19093               item.align[i] = null;
19094             }
19095           }
19096     
19097           for (i = 0; i < item.cells.length; i++) {
19098             item.cells[i] = item.cells[i]
19099               .replace(/^ *\| *| *\| *$/g, '')
19100               .split(/ *\| */);
19101           }
19102     
19103           this.tokens.push(item);
19104     
19105           continue;
19106         }
19107     
19108         // top-level paragraph
19109         if (top && (cap = this.rules.paragraph.exec(src))) {
19110           src = src.substring(cap[0].length);
19111           this.tokens.push({
19112             type: 'paragraph',
19113             text: cap[1].charAt(cap[1].length - 1) === '\n'
19114               ? cap[1].slice(0, -1)
19115               : cap[1]
19116           });
19117           continue;
19118         }
19119     
19120         // text
19121         if (cap = this.rules.text.exec(src)) {
19122           // Top-level should never reach here.
19123           src = src.substring(cap[0].length);
19124           this.tokens.push({
19125             type: 'text',
19126             text: cap[0]
19127           });
19128           continue;
19129         }
19130     
19131         if (src) {
19132           throw new
19133             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19134         }
19135       }
19136     
19137       return this.tokens;
19138     };
19139     
19140     /**
19141      * Inline-Level Grammar
19142      */
19143     
19144     var inline = {
19145       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19146       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19147       url: noop,
19148       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19149       link: /^!?\[(inside)\]\(href\)/,
19150       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19151       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19152       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19153       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19154       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19155       br: /^ {2,}\n(?!\s*$)/,
19156       del: noop,
19157       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19158     };
19159     
19160     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19161     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19162     
19163     inline.link = replace(inline.link)
19164       ('inside', inline._inside)
19165       ('href', inline._href)
19166       ();
19167     
19168     inline.reflink = replace(inline.reflink)
19169       ('inside', inline._inside)
19170       ();
19171     
19172     /**
19173      * Normal Inline Grammar
19174      */
19175     
19176     inline.normal = merge({}, inline);
19177     
19178     /**
19179      * Pedantic Inline Grammar
19180      */
19181     
19182     inline.pedantic = merge({}, inline.normal, {
19183       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19184       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19185     });
19186     
19187     /**
19188      * GFM Inline Grammar
19189      */
19190     
19191     inline.gfm = merge({}, inline.normal, {
19192       escape: replace(inline.escape)('])', '~|])')(),
19193       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19194       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19195       text: replace(inline.text)
19196         (']|', '~]|')
19197         ('|', '|https?://|')
19198         ()
19199     });
19200     
19201     /**
19202      * GFM + Line Breaks Inline Grammar
19203      */
19204     
19205     inline.breaks = merge({}, inline.gfm, {
19206       br: replace(inline.br)('{2,}', '*')(),
19207       text: replace(inline.gfm.text)('{2,}', '*')()
19208     });
19209     
19210     /**
19211      * Inline Lexer & Compiler
19212      */
19213     
19214     var InlineLexer  = function (links, options) {
19215       this.options = options || marked.defaults;
19216       this.links = links;
19217       this.rules = inline.normal;
19218       this.renderer = this.options.renderer || new Renderer;
19219       this.renderer.options = this.options;
19220     
19221       if (!this.links) {
19222         throw new
19223           Error('Tokens array requires a `links` property.');
19224       }
19225     
19226       if (this.options.gfm) {
19227         if (this.options.breaks) {
19228           this.rules = inline.breaks;
19229         } else {
19230           this.rules = inline.gfm;
19231         }
19232       } else if (this.options.pedantic) {
19233         this.rules = inline.pedantic;
19234       }
19235     }
19236     
19237     /**
19238      * Expose Inline Rules
19239      */
19240     
19241     InlineLexer.rules = inline;
19242     
19243     /**
19244      * Static Lexing/Compiling Method
19245      */
19246     
19247     InlineLexer.output = function(src, links, options) {
19248       var inline = new InlineLexer(links, options);
19249       return inline.output(src);
19250     };
19251     
19252     /**
19253      * Lexing/Compiling
19254      */
19255     
19256     InlineLexer.prototype.output = function(src) {
19257       var out = ''
19258         , link
19259         , text
19260         , href
19261         , cap;
19262     
19263       while (src) {
19264         // escape
19265         if (cap = this.rules.escape.exec(src)) {
19266           src = src.substring(cap[0].length);
19267           out += cap[1];
19268           continue;
19269         }
19270     
19271         // autolink
19272         if (cap = this.rules.autolink.exec(src)) {
19273           src = src.substring(cap[0].length);
19274           if (cap[2] === '@') {
19275             text = cap[1].charAt(6) === ':'
19276               ? this.mangle(cap[1].substring(7))
19277               : this.mangle(cap[1]);
19278             href = this.mangle('mailto:') + text;
19279           } else {
19280             text = escape(cap[1]);
19281             href = text;
19282           }
19283           out += this.renderer.link(href, null, text);
19284           continue;
19285         }
19286     
19287         // url (gfm)
19288         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19289           src = src.substring(cap[0].length);
19290           text = escape(cap[1]);
19291           href = text;
19292           out += this.renderer.link(href, null, text);
19293           continue;
19294         }
19295     
19296         // tag
19297         if (cap = this.rules.tag.exec(src)) {
19298           if (!this.inLink && /^<a /i.test(cap[0])) {
19299             this.inLink = true;
19300           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19301             this.inLink = false;
19302           }
19303           src = src.substring(cap[0].length);
19304           out += this.options.sanitize
19305             ? this.options.sanitizer
19306               ? this.options.sanitizer(cap[0])
19307               : escape(cap[0])
19308             : cap[0];
19309           continue;
19310         }
19311     
19312         // link
19313         if (cap = this.rules.link.exec(src)) {
19314           src = src.substring(cap[0].length);
19315           this.inLink = true;
19316           out += this.outputLink(cap, {
19317             href: cap[2],
19318             title: cap[3]
19319           });
19320           this.inLink = false;
19321           continue;
19322         }
19323     
19324         // reflink, nolink
19325         if ((cap = this.rules.reflink.exec(src))
19326             || (cap = this.rules.nolink.exec(src))) {
19327           src = src.substring(cap[0].length);
19328           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19329           link = this.links[link.toLowerCase()];
19330           if (!link || !link.href) {
19331             out += cap[0].charAt(0);
19332             src = cap[0].substring(1) + src;
19333             continue;
19334           }
19335           this.inLink = true;
19336           out += this.outputLink(cap, link);
19337           this.inLink = false;
19338           continue;
19339         }
19340     
19341         // strong
19342         if (cap = this.rules.strong.exec(src)) {
19343           src = src.substring(cap[0].length);
19344           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19345           continue;
19346         }
19347     
19348         // em
19349         if (cap = this.rules.em.exec(src)) {
19350           src = src.substring(cap[0].length);
19351           out += this.renderer.em(this.output(cap[2] || cap[1]));
19352           continue;
19353         }
19354     
19355         // code
19356         if (cap = this.rules.code.exec(src)) {
19357           src = src.substring(cap[0].length);
19358           out += this.renderer.codespan(escape(cap[2], true));
19359           continue;
19360         }
19361     
19362         // br
19363         if (cap = this.rules.br.exec(src)) {
19364           src = src.substring(cap[0].length);
19365           out += this.renderer.br();
19366           continue;
19367         }
19368     
19369         // del (gfm)
19370         if (cap = this.rules.del.exec(src)) {
19371           src = src.substring(cap[0].length);
19372           out += this.renderer.del(this.output(cap[1]));
19373           continue;
19374         }
19375     
19376         // text
19377         if (cap = this.rules.text.exec(src)) {
19378           src = src.substring(cap[0].length);
19379           out += this.renderer.text(escape(this.smartypants(cap[0])));
19380           continue;
19381         }
19382     
19383         if (src) {
19384           throw new
19385             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19386         }
19387       }
19388     
19389       return out;
19390     };
19391     
19392     /**
19393      * Compile Link
19394      */
19395     
19396     InlineLexer.prototype.outputLink = function(cap, link) {
19397       var href = escape(link.href)
19398         , title = link.title ? escape(link.title) : null;
19399     
19400       return cap[0].charAt(0) !== '!'
19401         ? this.renderer.link(href, title, this.output(cap[1]))
19402         : this.renderer.image(href, title, escape(cap[1]));
19403     };
19404     
19405     /**
19406      * Smartypants Transformations
19407      */
19408     
19409     InlineLexer.prototype.smartypants = function(text) {
19410       if (!this.options.smartypants)  { return text; }
19411       return text
19412         // em-dashes
19413         .replace(/---/g, '\u2014')
19414         // en-dashes
19415         .replace(/--/g, '\u2013')
19416         // opening singles
19417         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19418         // closing singles & apostrophes
19419         .replace(/'/g, '\u2019')
19420         // opening doubles
19421         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19422         // closing doubles
19423         .replace(/"/g, '\u201d')
19424         // ellipses
19425         .replace(/\.{3}/g, '\u2026');
19426     };
19427     
19428     /**
19429      * Mangle Links
19430      */
19431     
19432     InlineLexer.prototype.mangle = function(text) {
19433       if (!this.options.mangle) { return text; }
19434       var out = ''
19435         , l = text.length
19436         , i = 0
19437         , ch;
19438     
19439       for (; i < l; i++) {
19440         ch = text.charCodeAt(i);
19441         if (Math.random() > 0.5) {
19442           ch = 'x' + ch.toString(16);
19443         }
19444         out += '&#' + ch + ';';
19445       }
19446     
19447       return out;
19448     };
19449     
19450     /**
19451      * Renderer
19452      */
19453     
19454      /**
19455          * eval:var:Renderer
19456     */
19457     
19458     var Renderer   = function (options) {
19459       this.options = options || {};
19460     }
19461     
19462     Renderer.prototype.code = function(code, lang, escaped) {
19463       if (this.options.highlight) {
19464         var out = this.options.highlight(code, lang);
19465         if (out != null && out !== code) {
19466           escaped = true;
19467           code = out;
19468         }
19469       } else {
19470             // hack!!! - it's already escapeD?
19471             escaped = true;
19472       }
19473     
19474       if (!lang) {
19475         return '<pre><code>'
19476           + (escaped ? code : escape(code, true))
19477           + '\n</code></pre>';
19478       }
19479     
19480       return '<pre><code class="'
19481         + this.options.langPrefix
19482         + escape(lang, true)
19483         + '">'
19484         + (escaped ? code : escape(code, true))
19485         + '\n</code></pre>\n';
19486     };
19487     
19488     Renderer.prototype.blockquote = function(quote) {
19489       return '<blockquote>\n' + quote + '</blockquote>\n';
19490     };
19491     
19492     Renderer.prototype.html = function(html) {
19493       return html;
19494     };
19495     
19496     Renderer.prototype.heading = function(text, level, raw) {
19497       return '<h'
19498         + level
19499         + ' id="'
19500         + this.options.headerPrefix
19501         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19502         + '">'
19503         + text
19504         + '</h'
19505         + level
19506         + '>\n';
19507     };
19508     
19509     Renderer.prototype.hr = function() {
19510       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19511     };
19512     
19513     Renderer.prototype.list = function(body, ordered) {
19514       var type = ordered ? 'ol' : 'ul';
19515       return '<' + type + '>\n' + body + '</' + type + '>\n';
19516     };
19517     
19518     Renderer.prototype.listitem = function(text) {
19519       return '<li>' + text + '</li>\n';
19520     };
19521     
19522     Renderer.prototype.paragraph = function(text) {
19523       return '<p>' + text + '</p>\n';
19524     };
19525     
19526     Renderer.prototype.table = function(header, body) {
19527       return '<table class="table table-striped">\n'
19528         + '<thead>\n'
19529         + header
19530         + '</thead>\n'
19531         + '<tbody>\n'
19532         + body
19533         + '</tbody>\n'
19534         + '</table>\n';
19535     };
19536     
19537     Renderer.prototype.tablerow = function(content) {
19538       return '<tr>\n' + content + '</tr>\n';
19539     };
19540     
19541     Renderer.prototype.tablecell = function(content, flags) {
19542       var type = flags.header ? 'th' : 'td';
19543       var tag = flags.align
19544         ? '<' + type + ' style="text-align:' + flags.align + '">'
19545         : '<' + type + '>';
19546       return tag + content + '</' + type + '>\n';
19547     };
19548     
19549     // span level renderer
19550     Renderer.prototype.strong = function(text) {
19551       return '<strong>' + text + '</strong>';
19552     };
19553     
19554     Renderer.prototype.em = function(text) {
19555       return '<em>' + text + '</em>';
19556     };
19557     
19558     Renderer.prototype.codespan = function(text) {
19559       return '<code>' + text + '</code>';
19560     };
19561     
19562     Renderer.prototype.br = function() {
19563       return this.options.xhtml ? '<br/>' : '<br>';
19564     };
19565     
19566     Renderer.prototype.del = function(text) {
19567       return '<del>' + text + '</del>';
19568     };
19569     
19570     Renderer.prototype.link = function(href, title, text) {
19571       if (this.options.sanitize) {
19572         try {
19573           var prot = decodeURIComponent(unescape(href))
19574             .replace(/[^\w:]/g, '')
19575             .toLowerCase();
19576         } catch (e) {
19577           return '';
19578         }
19579         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19580           return '';
19581         }
19582       }
19583       var out = '<a href="' + href + '"';
19584       if (title) {
19585         out += ' title="' + title + '"';
19586       }
19587       out += '>' + text + '</a>';
19588       return out;
19589     };
19590     
19591     Renderer.prototype.image = function(href, title, text) {
19592       var out = '<img src="' + href + '" alt="' + text + '"';
19593       if (title) {
19594         out += ' title="' + title + '"';
19595       }
19596       out += this.options.xhtml ? '/>' : '>';
19597       return out;
19598     };
19599     
19600     Renderer.prototype.text = function(text) {
19601       return text;
19602     };
19603     
19604     /**
19605      * Parsing & Compiling
19606      */
19607          /**
19608          * eval:var:Parser
19609     */
19610     
19611     var Parser= function (options) {
19612       this.tokens = [];
19613       this.token = null;
19614       this.options = options || marked.defaults;
19615       this.options.renderer = this.options.renderer || new Renderer;
19616       this.renderer = this.options.renderer;
19617       this.renderer.options = this.options;
19618     }
19619     
19620     /**
19621      * Static Parse Method
19622      */
19623     
19624     Parser.parse = function(src, options, renderer) {
19625       var parser = new Parser(options, renderer);
19626       return parser.parse(src);
19627     };
19628     
19629     /**
19630      * Parse Loop
19631      */
19632     
19633     Parser.prototype.parse = function(src) {
19634       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19635       this.tokens = src.reverse();
19636     
19637       var out = '';
19638       while (this.next()) {
19639         out += this.tok();
19640       }
19641     
19642       return out;
19643     };
19644     
19645     /**
19646      * Next Token
19647      */
19648     
19649     Parser.prototype.next = function() {
19650       return this.token = this.tokens.pop();
19651     };
19652     
19653     /**
19654      * Preview Next Token
19655      */
19656     
19657     Parser.prototype.peek = function() {
19658       return this.tokens[this.tokens.length - 1] || 0;
19659     };
19660     
19661     /**
19662      * Parse Text Tokens
19663      */
19664     
19665     Parser.prototype.parseText = function() {
19666       var body = this.token.text;
19667     
19668       while (this.peek().type === 'text') {
19669         body += '\n' + this.next().text;
19670       }
19671     
19672       return this.inline.output(body);
19673     };
19674     
19675     /**
19676      * Parse Current Token
19677      */
19678     
19679     Parser.prototype.tok = function() {
19680       switch (this.token.type) {
19681         case 'space': {
19682           return '';
19683         }
19684         case 'hr': {
19685           return this.renderer.hr();
19686         }
19687         case 'heading': {
19688           return this.renderer.heading(
19689             this.inline.output(this.token.text),
19690             this.token.depth,
19691             this.token.text);
19692         }
19693         case 'code': {
19694           return this.renderer.code(this.token.text,
19695             this.token.lang,
19696             this.token.escaped);
19697         }
19698         case 'table': {
19699           var header = ''
19700             , body = ''
19701             , i
19702             , row
19703             , cell
19704             , flags
19705             , j;
19706     
19707           // header
19708           cell = '';
19709           for (i = 0; i < this.token.header.length; i++) {
19710             flags = { header: true, align: this.token.align[i] };
19711             cell += this.renderer.tablecell(
19712               this.inline.output(this.token.header[i]),
19713               { header: true, align: this.token.align[i] }
19714             );
19715           }
19716           header += this.renderer.tablerow(cell);
19717     
19718           for (i = 0; i < this.token.cells.length; i++) {
19719             row = this.token.cells[i];
19720     
19721             cell = '';
19722             for (j = 0; j < row.length; j++) {
19723               cell += this.renderer.tablecell(
19724                 this.inline.output(row[j]),
19725                 { header: false, align: this.token.align[j] }
19726               );
19727             }
19728     
19729             body += this.renderer.tablerow(cell);
19730           }
19731           return this.renderer.table(header, body);
19732         }
19733         case 'blockquote_start': {
19734           var body = '';
19735     
19736           while (this.next().type !== 'blockquote_end') {
19737             body += this.tok();
19738           }
19739     
19740           return this.renderer.blockquote(body);
19741         }
19742         case 'list_start': {
19743           var body = ''
19744             , ordered = this.token.ordered;
19745     
19746           while (this.next().type !== 'list_end') {
19747             body += this.tok();
19748           }
19749     
19750           return this.renderer.list(body, ordered);
19751         }
19752         case 'list_item_start': {
19753           var body = '';
19754     
19755           while (this.next().type !== 'list_item_end') {
19756             body += this.token.type === 'text'
19757               ? this.parseText()
19758               : this.tok();
19759           }
19760     
19761           return this.renderer.listitem(body);
19762         }
19763         case 'loose_item_start': {
19764           var body = '';
19765     
19766           while (this.next().type !== 'list_item_end') {
19767             body += this.tok();
19768           }
19769     
19770           return this.renderer.listitem(body);
19771         }
19772         case 'html': {
19773           var html = !this.token.pre && !this.options.pedantic
19774             ? this.inline.output(this.token.text)
19775             : this.token.text;
19776           return this.renderer.html(html);
19777         }
19778         case 'paragraph': {
19779           return this.renderer.paragraph(this.inline.output(this.token.text));
19780         }
19781         case 'text': {
19782           return this.renderer.paragraph(this.parseText());
19783         }
19784       }
19785     };
19786   
19787     
19788     /**
19789      * Marked
19790      */
19791          /**
19792          * eval:var:marked
19793     */
19794     var marked = function (src, opt, callback) {
19795       if (callback || typeof opt === 'function') {
19796         if (!callback) {
19797           callback = opt;
19798           opt = null;
19799         }
19800     
19801         opt = merge({}, marked.defaults, opt || {});
19802     
19803         var highlight = opt.highlight
19804           , tokens
19805           , pending
19806           , i = 0;
19807     
19808         try {
19809           tokens = Lexer.lex(src, opt)
19810         } catch (e) {
19811           return callback(e);
19812         }
19813     
19814         pending = tokens.length;
19815          /**
19816          * eval:var:done
19817     */
19818         var done = function(err) {
19819           if (err) {
19820             opt.highlight = highlight;
19821             return callback(err);
19822           }
19823     
19824           var out;
19825     
19826           try {
19827             out = Parser.parse(tokens, opt);
19828           } catch (e) {
19829             err = e;
19830           }
19831     
19832           opt.highlight = highlight;
19833     
19834           return err
19835             ? callback(err)
19836             : callback(null, out);
19837         };
19838     
19839         if (!highlight || highlight.length < 3) {
19840           return done();
19841         }
19842     
19843         delete opt.highlight;
19844     
19845         if (!pending) { return done(); }
19846     
19847         for (; i < tokens.length; i++) {
19848           (function(token) {
19849             if (token.type !== 'code') {
19850               return --pending || done();
19851             }
19852             return highlight(token.text, token.lang, function(err, code) {
19853               if (err) { return done(err); }
19854               if (code == null || code === token.text) {
19855                 return --pending || done();
19856               }
19857               token.text = code;
19858               token.escaped = true;
19859               --pending || done();
19860             });
19861           })(tokens[i]);
19862         }
19863     
19864         return;
19865       }
19866       try {
19867         if (opt) { opt = merge({}, marked.defaults, opt); }
19868         return Parser.parse(Lexer.lex(src, opt), opt);
19869       } catch (e) {
19870         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19871         if ((opt || marked.defaults).silent) {
19872           return '<p>An error occured:</p><pre>'
19873             + escape(e.message + '', true)
19874             + '</pre>';
19875         }
19876         throw e;
19877       }
19878     }
19879     
19880     /**
19881      * Options
19882      */
19883     
19884     marked.options =
19885     marked.setOptions = function(opt) {
19886       merge(marked.defaults, opt);
19887       return marked;
19888     };
19889     
19890     marked.defaults = {
19891       gfm: true,
19892       tables: true,
19893       breaks: false,
19894       pedantic: false,
19895       sanitize: false,
19896       sanitizer: null,
19897       mangle: true,
19898       smartLists: false,
19899       silent: false,
19900       highlight: null,
19901       langPrefix: 'lang-',
19902       smartypants: false,
19903       headerPrefix: '',
19904       renderer: new Renderer,
19905       xhtml: false
19906     };
19907     
19908     /**
19909      * Expose
19910      */
19911     
19912     marked.Parser = Parser;
19913     marked.parser = Parser.parse;
19914     
19915     marked.Renderer = Renderer;
19916     
19917     marked.Lexer = Lexer;
19918     marked.lexer = Lexer.lex;
19919     
19920     marked.InlineLexer = InlineLexer;
19921     marked.inlineLexer = InlineLexer.output;
19922     
19923     marked.parse = marked;
19924     
19925     Roo.Markdown.marked = marked;
19926
19927 })();/*
19928  * Based on:
19929  * Ext JS Library 1.1.1
19930  * Copyright(c) 2006-2007, Ext JS, LLC.
19931  *
19932  * Originally Released Under LGPL - original licence link has changed is not relivant.
19933  *
19934  * Fork - LGPL
19935  * <script type="text/javascript">
19936  */
19937
19938
19939
19940 /*
19941  * These classes are derivatives of the similarly named classes in the YUI Library.
19942  * The original license:
19943  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19944  * Code licensed under the BSD License:
19945  * http://developer.yahoo.net/yui/license.txt
19946  */
19947
19948 (function() {
19949
19950 var Event=Roo.EventManager;
19951 var Dom=Roo.lib.Dom;
19952
19953 /**
19954  * @class Roo.dd.DragDrop
19955  * @extends Roo.util.Observable
19956  * Defines the interface and base operation of items that that can be
19957  * dragged or can be drop targets.  It was designed to be extended, overriding
19958  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19959  * Up to three html elements can be associated with a DragDrop instance:
19960  * <ul>
19961  * <li>linked element: the element that is passed into the constructor.
19962  * This is the element which defines the boundaries for interaction with
19963  * other DragDrop objects.</li>
19964  * <li>handle element(s): The drag operation only occurs if the element that
19965  * was clicked matches a handle element.  By default this is the linked
19966  * element, but there are times that you will want only a portion of the
19967  * linked element to initiate the drag operation, and the setHandleElId()
19968  * method provides a way to define this.</li>
19969  * <li>drag element: this represents the element that would be moved along
19970  * with the cursor during a drag operation.  By default, this is the linked
19971  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19972  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19973  * </li>
19974  * </ul>
19975  * This class should not be instantiated until the onload event to ensure that
19976  * the associated elements are available.
19977  * The following would define a DragDrop obj that would interact with any
19978  * other DragDrop obj in the "group1" group:
19979  * <pre>
19980  *  dd = new Roo.dd.DragDrop("div1", "group1");
19981  * </pre>
19982  * Since none of the event handlers have been implemented, nothing would
19983  * actually happen if you were to run the code above.  Normally you would
19984  * override this class or one of the default implementations, but you can
19985  * also override the methods you want on an instance of the class...
19986  * <pre>
19987  *  dd.onDragDrop = function(e, id) {
19988  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19989  *  }
19990  * </pre>
19991  * @constructor
19992  * @param {String} id of the element that is linked to this instance
19993  * @param {String} sGroup the group of related DragDrop objects
19994  * @param {object} config an object containing configurable attributes
19995  *                Valid properties for DragDrop:
19996  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19997  */
19998 Roo.dd.DragDrop = function(id, sGroup, config) {
19999     if (id) {
20000         this.init(id, sGroup, config);
20001     }
20002     
20003 };
20004
20005 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
20006
20007     /**
20008      * The id of the element associated with this object.  This is what we
20009      * refer to as the "linked element" because the size and position of
20010      * this element is used to determine when the drag and drop objects have
20011      * interacted.
20012      * @property id
20013      * @type String
20014      */
20015     id: null,
20016
20017     /**
20018      * Configuration attributes passed into the constructor
20019      * @property config
20020      * @type object
20021      */
20022     config: null,
20023
20024     /**
20025      * The id of the element that will be dragged.  By default this is same
20026      * as the linked element , but could be changed to another element. Ex:
20027      * Roo.dd.DDProxy
20028      * @property dragElId
20029      * @type String
20030      * @private
20031      */
20032     dragElId: null,
20033
20034     /**
20035      * the id of the element that initiates the drag operation.  By default
20036      * this is the linked element, but could be changed to be a child of this
20037      * element.  This lets us do things like only starting the drag when the
20038      * header element within the linked html element is clicked.
20039      * @property handleElId
20040      * @type String
20041      * @private
20042      */
20043     handleElId: null,
20044
20045     /**
20046      * An associative array of HTML tags that will be ignored if clicked.
20047      * @property invalidHandleTypes
20048      * @type {string: string}
20049      */
20050     invalidHandleTypes: null,
20051
20052     /**
20053      * An associative array of ids for elements that will be ignored if clicked
20054      * @property invalidHandleIds
20055      * @type {string: string}
20056      */
20057     invalidHandleIds: null,
20058
20059     /**
20060      * An indexted array of css class names for elements that will be ignored
20061      * if clicked.
20062      * @property invalidHandleClasses
20063      * @type string[]
20064      */
20065     invalidHandleClasses: null,
20066
20067     /**
20068      * The linked element's absolute X position at the time the drag was
20069      * started
20070      * @property startPageX
20071      * @type int
20072      * @private
20073      */
20074     startPageX: 0,
20075
20076     /**
20077      * The linked element's absolute X position at the time the drag was
20078      * started
20079      * @property startPageY
20080      * @type int
20081      * @private
20082      */
20083     startPageY: 0,
20084
20085     /**
20086      * The group defines a logical collection of DragDrop objects that are
20087      * related.  Instances only get events when interacting with other
20088      * DragDrop object in the same group.  This lets us define multiple
20089      * groups using a single DragDrop subclass if we want.
20090      * @property groups
20091      * @type {string: string}
20092      */
20093     groups: null,
20094
20095     /**
20096      * Individual drag/drop instances can be locked.  This will prevent
20097      * onmousedown start drag.
20098      * @property locked
20099      * @type boolean
20100      * @private
20101      */
20102     locked: false,
20103
20104     /**
20105      * Lock this instance
20106      * @method lock
20107      */
20108     lock: function() { this.locked = true; },
20109
20110     /**
20111      * Unlock this instace
20112      * @method unlock
20113      */
20114     unlock: function() { this.locked = false; },
20115
20116     /**
20117      * By default, all insances can be a drop target.  This can be disabled by
20118      * setting isTarget to false.
20119      * @method isTarget
20120      * @type boolean
20121      */
20122     isTarget: true,
20123
20124     /**
20125      * The padding configured for this drag and drop object for calculating
20126      * the drop zone intersection with this object.
20127      * @method padding
20128      * @type int[]
20129      */
20130     padding: null,
20131
20132     /**
20133      * Cached reference to the linked element
20134      * @property _domRef
20135      * @private
20136      */
20137     _domRef: null,
20138
20139     /**
20140      * Internal typeof flag
20141      * @property __ygDragDrop
20142      * @private
20143      */
20144     __ygDragDrop: true,
20145
20146     /**
20147      * Set to true when horizontal contraints are applied
20148      * @property constrainX
20149      * @type boolean
20150      * @private
20151      */
20152     constrainX: false,
20153
20154     /**
20155      * Set to true when vertical contraints are applied
20156      * @property constrainY
20157      * @type boolean
20158      * @private
20159      */
20160     constrainY: false,
20161
20162     /**
20163      * The left constraint
20164      * @property minX
20165      * @type int
20166      * @private
20167      */
20168     minX: 0,
20169
20170     /**
20171      * The right constraint
20172      * @property maxX
20173      * @type int
20174      * @private
20175      */
20176     maxX: 0,
20177
20178     /**
20179      * The up constraint
20180      * @property minY
20181      * @type int
20182      * @type int
20183      * @private
20184      */
20185     minY: 0,
20186
20187     /**
20188      * The down constraint
20189      * @property maxY
20190      * @type int
20191      * @private
20192      */
20193     maxY: 0,
20194
20195     /**
20196      * Maintain offsets when we resetconstraints.  Set to true when you want
20197      * the position of the element relative to its parent to stay the same
20198      * when the page changes
20199      *
20200      * @property maintainOffset
20201      * @type boolean
20202      */
20203     maintainOffset: false,
20204
20205     /**
20206      * Array of pixel locations the element will snap to if we specified a
20207      * horizontal graduation/interval.  This array is generated automatically
20208      * when you define a tick interval.
20209      * @property xTicks
20210      * @type int[]
20211      */
20212     xTicks: null,
20213
20214     /**
20215      * Array of pixel locations the element will snap to if we specified a
20216      * vertical graduation/interval.  This array is generated automatically
20217      * when you define a tick interval.
20218      * @property yTicks
20219      * @type int[]
20220      */
20221     yTicks: null,
20222
20223     /**
20224      * By default the drag and drop instance will only respond to the primary
20225      * button click (left button for a right-handed mouse).  Set to true to
20226      * allow drag and drop to start with any mouse click that is propogated
20227      * by the browser
20228      * @property primaryButtonOnly
20229      * @type boolean
20230      */
20231     primaryButtonOnly: true,
20232
20233     /**
20234      * The availabe property is false until the linked dom element is accessible.
20235      * @property available
20236      * @type boolean
20237      */
20238     available: false,
20239
20240     /**
20241      * By default, drags can only be initiated if the mousedown occurs in the
20242      * region the linked element is.  This is done in part to work around a
20243      * bug in some browsers that mis-report the mousedown if the previous
20244      * mouseup happened outside of the window.  This property is set to true
20245      * if outer handles are defined.
20246      *
20247      * @property hasOuterHandles
20248      * @type boolean
20249      * @default false
20250      */
20251     hasOuterHandles: false,
20252
20253     /**
20254      * Code that executes immediately before the startDrag event
20255      * @method b4StartDrag
20256      * @private
20257      */
20258     b4StartDrag: function(x, y) { },
20259
20260     /**
20261      * Abstract method called after a drag/drop object is clicked
20262      * and the drag or mousedown time thresholds have beeen met.
20263      * @method startDrag
20264      * @param {int} X click location
20265      * @param {int} Y click location
20266      */
20267     startDrag: function(x, y) { /* override this */ },
20268
20269     /**
20270      * Code that executes immediately before the onDrag event
20271      * @method b4Drag
20272      * @private
20273      */
20274     b4Drag: function(e) { },
20275
20276     /**
20277      * Abstract method called during the onMouseMove event while dragging an
20278      * object.
20279      * @method onDrag
20280      * @param {Event} e the mousemove event
20281      */
20282     onDrag: function(e) { /* override this */ },
20283
20284     /**
20285      * Abstract method called when this element fist begins hovering over
20286      * another DragDrop obj
20287      * @method onDragEnter
20288      * @param {Event} e the mousemove event
20289      * @param {String|DragDrop[]} id In POINT mode, the element
20290      * id this is hovering over.  In INTERSECT mode, an array of one or more
20291      * dragdrop items being hovered over.
20292      */
20293     onDragEnter: function(e, id) { /* override this */ },
20294
20295     /**
20296      * Code that executes immediately before the onDragOver event
20297      * @method b4DragOver
20298      * @private
20299      */
20300     b4DragOver: function(e) { },
20301
20302     /**
20303      * Abstract method called when this element is hovering over another
20304      * DragDrop obj
20305      * @method onDragOver
20306      * @param {Event} e the mousemove event
20307      * @param {String|DragDrop[]} id In POINT mode, the element
20308      * id this is hovering over.  In INTERSECT mode, an array of dd items
20309      * being hovered over.
20310      */
20311     onDragOver: function(e, id) { /* override this */ },
20312
20313     /**
20314      * Code that executes immediately before the onDragOut event
20315      * @method b4DragOut
20316      * @private
20317      */
20318     b4DragOut: function(e) { },
20319
20320     /**
20321      * Abstract method called when we are no longer hovering over an element
20322      * @method onDragOut
20323      * @param {Event} e the mousemove event
20324      * @param {String|DragDrop[]} id In POINT mode, the element
20325      * id this was hovering over.  In INTERSECT mode, an array of dd items
20326      * that the mouse is no longer over.
20327      */
20328     onDragOut: function(e, id) { /* override this */ },
20329
20330     /**
20331      * Code that executes immediately before the onDragDrop event
20332      * @method b4DragDrop
20333      * @private
20334      */
20335     b4DragDrop: function(e) { },
20336
20337     /**
20338      * Abstract method called when this item is dropped on another DragDrop
20339      * obj
20340      * @method onDragDrop
20341      * @param {Event} e the mouseup event
20342      * @param {String|DragDrop[]} id In POINT mode, the element
20343      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20344      * was dropped on.
20345      */
20346     onDragDrop: function(e, id) { /* override this */ },
20347
20348     /**
20349      * Abstract method called when this item is dropped on an area with no
20350      * drop target
20351      * @method onInvalidDrop
20352      * @param {Event} e the mouseup event
20353      */
20354     onInvalidDrop: function(e) { /* override this */ },
20355
20356     /**
20357      * Code that executes immediately before the endDrag event
20358      * @method b4EndDrag
20359      * @private
20360      */
20361     b4EndDrag: function(e) { },
20362
20363     /**
20364      * Fired when we are done dragging the object
20365      * @method endDrag
20366      * @param {Event} e the mouseup event
20367      */
20368     endDrag: function(e) { /* override this */ },
20369
20370     /**
20371      * Code executed immediately before the onMouseDown event
20372      * @method b4MouseDown
20373      * @param {Event} e the mousedown event
20374      * @private
20375      */
20376     b4MouseDown: function(e) {  },
20377
20378     /**
20379      * Event handler that fires when a drag/drop obj gets a mousedown
20380      * @method onMouseDown
20381      * @param {Event} e the mousedown event
20382      */
20383     onMouseDown: function(e) { /* override this */ },
20384
20385     /**
20386      * Event handler that fires when a drag/drop obj gets a mouseup
20387      * @method onMouseUp
20388      * @param {Event} e the mouseup event
20389      */
20390     onMouseUp: function(e) { /* override this */ },
20391
20392     /**
20393      * Override the onAvailable method to do what is needed after the initial
20394      * position was determined.
20395      * @method onAvailable
20396      */
20397     onAvailable: function () {
20398     },
20399
20400     /*
20401      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20402      * @type Object
20403      */
20404     defaultPadding : {left:0, right:0, top:0, bottom:0},
20405
20406     /*
20407      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20408  *
20409  * Usage:
20410  <pre><code>
20411  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20412                 { dragElId: "existingProxyDiv" });
20413  dd.startDrag = function(){
20414      this.constrainTo("parent-id");
20415  };
20416  </code></pre>
20417  * Or you can initalize it using the {@link Roo.Element} object:
20418  <pre><code>
20419  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20420      startDrag : function(){
20421          this.constrainTo("parent-id");
20422      }
20423  });
20424  </code></pre>
20425      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20426      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20427      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20428      * an object containing the sides to pad. For example: {right:10, bottom:10}
20429      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20430      */
20431     constrainTo : function(constrainTo, pad, inContent){
20432         if(typeof pad == "number"){
20433             pad = {left: pad, right:pad, top:pad, bottom:pad};
20434         }
20435         pad = pad || this.defaultPadding;
20436         var b = Roo.get(this.getEl()).getBox();
20437         var ce = Roo.get(constrainTo);
20438         var s = ce.getScroll();
20439         var c, cd = ce.dom;
20440         if(cd == document.body){
20441             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20442         }else{
20443             xy = ce.getXY();
20444             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20445         }
20446
20447
20448         var topSpace = b.y - c.y;
20449         var leftSpace = b.x - c.x;
20450
20451         this.resetConstraints();
20452         this.setXConstraint(leftSpace - (pad.left||0), // left
20453                 c.width - leftSpace - b.width - (pad.right||0) //right
20454         );
20455         this.setYConstraint(topSpace - (pad.top||0), //top
20456                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20457         );
20458     },
20459
20460     /**
20461      * Returns a reference to the linked element
20462      * @method getEl
20463      * @return {HTMLElement} the html element
20464      */
20465     getEl: function() {
20466         if (!this._domRef) {
20467             this._domRef = Roo.getDom(this.id);
20468         }
20469
20470         return this._domRef;
20471     },
20472
20473     /**
20474      * Returns a reference to the actual element to drag.  By default this is
20475      * the same as the html element, but it can be assigned to another
20476      * element. An example of this can be found in Roo.dd.DDProxy
20477      * @method getDragEl
20478      * @return {HTMLElement} the html element
20479      */
20480     getDragEl: function() {
20481         return Roo.getDom(this.dragElId);
20482     },
20483
20484     /**
20485      * Sets up the DragDrop object.  Must be called in the constructor of any
20486      * Roo.dd.DragDrop subclass
20487      * @method init
20488      * @param id the id of the linked element
20489      * @param {String} sGroup the group of related items
20490      * @param {object} config configuration attributes
20491      */
20492     init: function(id, sGroup, config) {
20493         this.initTarget(id, sGroup, config);
20494         if (!Roo.isTouch) {
20495             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20496         }
20497         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20498         // Event.on(this.id, "selectstart", Event.preventDefault);
20499     },
20500
20501     /**
20502      * Initializes Targeting functionality only... the object does not
20503      * get a mousedown handler.
20504      * @method initTarget
20505      * @param id the id of the linked element
20506      * @param {String} sGroup the group of related items
20507      * @param {object} config configuration attributes
20508      */
20509     initTarget: function(id, sGroup, config) {
20510
20511         // configuration attributes
20512         this.config = config || {};
20513
20514         // create a local reference to the drag and drop manager
20515         this.DDM = Roo.dd.DDM;
20516         // initialize the groups array
20517         this.groups = {};
20518
20519         // assume that we have an element reference instead of an id if the
20520         // parameter is not a string
20521         if (typeof id !== "string") {
20522             id = Roo.id(id);
20523         }
20524
20525         // set the id
20526         this.id = id;
20527
20528         // add to an interaction group
20529         this.addToGroup((sGroup) ? sGroup : "default");
20530
20531         // We don't want to register this as the handle with the manager
20532         // so we just set the id rather than calling the setter.
20533         this.handleElId = id;
20534
20535         // the linked element is the element that gets dragged by default
20536         this.setDragElId(id);
20537
20538         // by default, clicked anchors will not start drag operations.
20539         this.invalidHandleTypes = { A: "A" };
20540         this.invalidHandleIds = {};
20541         this.invalidHandleClasses = [];
20542
20543         this.applyConfig();
20544
20545         this.handleOnAvailable();
20546     },
20547
20548     /**
20549      * Applies the configuration parameters that were passed into the constructor.
20550      * This is supposed to happen at each level through the inheritance chain.  So
20551      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20552      * DragDrop in order to get all of the parameters that are available in
20553      * each object.
20554      * @method applyConfig
20555      */
20556     applyConfig: function() {
20557
20558         // configurable properties:
20559         //    padding, isTarget, maintainOffset, primaryButtonOnly
20560         this.padding           = this.config.padding || [0, 0, 0, 0];
20561         this.isTarget          = (this.config.isTarget !== false);
20562         this.maintainOffset    = (this.config.maintainOffset);
20563         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20564
20565     },
20566
20567     /**
20568      * Executed when the linked element is available
20569      * @method handleOnAvailable
20570      * @private
20571      */
20572     handleOnAvailable: function() {
20573         this.available = true;
20574         this.resetConstraints();
20575         this.onAvailable();
20576     },
20577
20578      /**
20579      * Configures the padding for the target zone in px.  Effectively expands
20580      * (or reduces) the virtual object size for targeting calculations.
20581      * Supports css-style shorthand; if only one parameter is passed, all sides
20582      * will have that padding, and if only two are passed, the top and bottom
20583      * will have the first param, the left and right the second.
20584      * @method setPadding
20585      * @param {int} iTop    Top pad
20586      * @param {int} iRight  Right pad
20587      * @param {int} iBot    Bot pad
20588      * @param {int} iLeft   Left pad
20589      */
20590     setPadding: function(iTop, iRight, iBot, iLeft) {
20591         // this.padding = [iLeft, iRight, iTop, iBot];
20592         if (!iRight && 0 !== iRight) {
20593             this.padding = [iTop, iTop, iTop, iTop];
20594         } else if (!iBot && 0 !== iBot) {
20595             this.padding = [iTop, iRight, iTop, iRight];
20596         } else {
20597             this.padding = [iTop, iRight, iBot, iLeft];
20598         }
20599     },
20600
20601     /**
20602      * Stores the initial placement of the linked element.
20603      * @method setInitialPosition
20604      * @param {int} diffX   the X offset, default 0
20605      * @param {int} diffY   the Y offset, default 0
20606      */
20607     setInitPosition: function(diffX, diffY) {
20608         var el = this.getEl();
20609
20610         if (!this.DDM.verifyEl(el)) {
20611             return;
20612         }
20613
20614         var dx = diffX || 0;
20615         var dy = diffY || 0;
20616
20617         var p = Dom.getXY( el );
20618
20619         this.initPageX = p[0] - dx;
20620         this.initPageY = p[1] - dy;
20621
20622         this.lastPageX = p[0];
20623         this.lastPageY = p[1];
20624
20625
20626         this.setStartPosition(p);
20627     },
20628
20629     /**
20630      * Sets the start position of the element.  This is set when the obj
20631      * is initialized, the reset when a drag is started.
20632      * @method setStartPosition
20633      * @param pos current position (from previous lookup)
20634      * @private
20635      */
20636     setStartPosition: function(pos) {
20637         var p = pos || Dom.getXY( this.getEl() );
20638         this.deltaSetXY = null;
20639
20640         this.startPageX = p[0];
20641         this.startPageY = p[1];
20642     },
20643
20644     /**
20645      * Add this instance to a group of related drag/drop objects.  All
20646      * instances belong to at least one group, and can belong to as many
20647      * groups as needed.
20648      * @method addToGroup
20649      * @param sGroup {string} the name of the group
20650      */
20651     addToGroup: function(sGroup) {
20652         this.groups[sGroup] = true;
20653         this.DDM.regDragDrop(this, sGroup);
20654     },
20655
20656     /**
20657      * Remove's this instance from the supplied interaction group
20658      * @method removeFromGroup
20659      * @param {string}  sGroup  The group to drop
20660      */
20661     removeFromGroup: function(sGroup) {
20662         if (this.groups[sGroup]) {
20663             delete this.groups[sGroup];
20664         }
20665
20666         this.DDM.removeDDFromGroup(this, sGroup);
20667     },
20668
20669     /**
20670      * Allows you to specify that an element other than the linked element
20671      * will be moved with the cursor during a drag
20672      * @method setDragElId
20673      * @param id {string} the id of the element that will be used to initiate the drag
20674      */
20675     setDragElId: function(id) {
20676         this.dragElId = id;
20677     },
20678
20679     /**
20680      * Allows you to specify a child of the linked element that should be
20681      * used to initiate the drag operation.  An example of this would be if
20682      * you have a content div with text and links.  Clicking anywhere in the
20683      * content area would normally start the drag operation.  Use this method
20684      * to specify that an element inside of the content div is the element
20685      * that starts the drag operation.
20686      * @method setHandleElId
20687      * @param id {string} the id of the element that will be used to
20688      * initiate the drag.
20689      */
20690     setHandleElId: function(id) {
20691         if (typeof id !== "string") {
20692             id = Roo.id(id);
20693         }
20694         this.handleElId = id;
20695         this.DDM.regHandle(this.id, id);
20696     },
20697
20698     /**
20699      * Allows you to set an element outside of the linked element as a drag
20700      * handle
20701      * @method setOuterHandleElId
20702      * @param id the id of the element that will be used to initiate the drag
20703      */
20704     setOuterHandleElId: function(id) {
20705         if (typeof id !== "string") {
20706             id = Roo.id(id);
20707         }
20708         Event.on(id, "mousedown",
20709                 this.handleMouseDown, this);
20710         this.setHandleElId(id);
20711
20712         this.hasOuterHandles = true;
20713     },
20714
20715     /**
20716      * Remove all drag and drop hooks for this element
20717      * @method unreg
20718      */
20719     unreg: function() {
20720         Event.un(this.id, "mousedown",
20721                 this.handleMouseDown);
20722         Event.un(this.id, "touchstart",
20723                 this.handleMouseDown);
20724         this._domRef = null;
20725         this.DDM._remove(this);
20726     },
20727
20728     destroy : function(){
20729         this.unreg();
20730     },
20731
20732     /**
20733      * Returns true if this instance is locked, or the drag drop mgr is locked
20734      * (meaning that all drag/drop is disabled on the page.)
20735      * @method isLocked
20736      * @return {boolean} true if this obj or all drag/drop is locked, else
20737      * false
20738      */
20739     isLocked: function() {
20740         return (this.DDM.isLocked() || this.locked);
20741     },
20742
20743     /**
20744      * Fired when this object is clicked
20745      * @method handleMouseDown
20746      * @param {Event} e
20747      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20748      * @private
20749      */
20750     handleMouseDown: function(e, oDD){
20751      
20752         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20753             //Roo.log('not touch/ button !=0');
20754             return;
20755         }
20756         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20757             return; // double touch..
20758         }
20759         
20760
20761         if (this.isLocked()) {
20762             //Roo.log('locked');
20763             return;
20764         }
20765
20766         this.DDM.refreshCache(this.groups);
20767 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20768         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20769         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20770             //Roo.log('no outer handes or not over target');
20771                 // do nothing.
20772         } else {
20773 //            Roo.log('check validator');
20774             if (this.clickValidator(e)) {
20775 //                Roo.log('validate success');
20776                 // set the initial element position
20777                 this.setStartPosition();
20778
20779
20780                 this.b4MouseDown(e);
20781                 this.onMouseDown(e);
20782
20783                 this.DDM.handleMouseDown(e, this);
20784
20785                 this.DDM.stopEvent(e);
20786             } else {
20787
20788
20789             }
20790         }
20791     },
20792
20793     clickValidator: function(e) {
20794         var target = e.getTarget();
20795         return ( this.isValidHandleChild(target) &&
20796                     (this.id == this.handleElId ||
20797                         this.DDM.handleWasClicked(target, this.id)) );
20798     },
20799
20800     /**
20801      * Allows you to specify a tag name that should not start a drag operation
20802      * when clicked.  This is designed to facilitate embedding links within a
20803      * drag handle that do something other than start the drag.
20804      * @method addInvalidHandleType
20805      * @param {string} tagName the type of element to exclude
20806      */
20807     addInvalidHandleType: function(tagName) {
20808         var type = tagName.toUpperCase();
20809         this.invalidHandleTypes[type] = type;
20810     },
20811
20812     /**
20813      * Lets you to specify an element id for a child of a drag handle
20814      * that should not initiate a drag
20815      * @method addInvalidHandleId
20816      * @param {string} id the element id of the element you wish to ignore
20817      */
20818     addInvalidHandleId: function(id) {
20819         if (typeof id !== "string") {
20820             id = Roo.id(id);
20821         }
20822         this.invalidHandleIds[id] = id;
20823     },
20824
20825     /**
20826      * Lets you specify a css class of elements that will not initiate a drag
20827      * @method addInvalidHandleClass
20828      * @param {string} cssClass the class of the elements you wish to ignore
20829      */
20830     addInvalidHandleClass: function(cssClass) {
20831         this.invalidHandleClasses.push(cssClass);
20832     },
20833
20834     /**
20835      * Unsets an excluded tag name set by addInvalidHandleType
20836      * @method removeInvalidHandleType
20837      * @param {string} tagName the type of element to unexclude
20838      */
20839     removeInvalidHandleType: function(tagName) {
20840         var type = tagName.toUpperCase();
20841         // this.invalidHandleTypes[type] = null;
20842         delete this.invalidHandleTypes[type];
20843     },
20844
20845     /**
20846      * Unsets an invalid handle id
20847      * @method removeInvalidHandleId
20848      * @param {string} id the id of the element to re-enable
20849      */
20850     removeInvalidHandleId: function(id) {
20851         if (typeof id !== "string") {
20852             id = Roo.id(id);
20853         }
20854         delete this.invalidHandleIds[id];
20855     },
20856
20857     /**
20858      * Unsets an invalid css class
20859      * @method removeInvalidHandleClass
20860      * @param {string} cssClass the class of the element(s) you wish to
20861      * re-enable
20862      */
20863     removeInvalidHandleClass: function(cssClass) {
20864         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20865             if (this.invalidHandleClasses[i] == cssClass) {
20866                 delete this.invalidHandleClasses[i];
20867             }
20868         }
20869     },
20870
20871     /**
20872      * Checks the tag exclusion list to see if this click should be ignored
20873      * @method isValidHandleChild
20874      * @param {HTMLElement} node the HTMLElement to evaluate
20875      * @return {boolean} true if this is a valid tag type, false if not
20876      */
20877     isValidHandleChild: function(node) {
20878
20879         var valid = true;
20880         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20881         var nodeName;
20882         try {
20883             nodeName = node.nodeName.toUpperCase();
20884         } catch(e) {
20885             nodeName = node.nodeName;
20886         }
20887         valid = valid && !this.invalidHandleTypes[nodeName];
20888         valid = valid && !this.invalidHandleIds[node.id];
20889
20890         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20891             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20892         }
20893
20894
20895         return valid;
20896
20897     },
20898
20899     /**
20900      * Create the array of horizontal tick marks if an interval was specified
20901      * in setXConstraint().
20902      * @method setXTicks
20903      * @private
20904      */
20905     setXTicks: function(iStartX, iTickSize) {
20906         this.xTicks = [];
20907         this.xTickSize = iTickSize;
20908
20909         var tickMap = {};
20910
20911         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20912             if (!tickMap[i]) {
20913                 this.xTicks[this.xTicks.length] = i;
20914                 tickMap[i] = true;
20915             }
20916         }
20917
20918         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20919             if (!tickMap[i]) {
20920                 this.xTicks[this.xTicks.length] = i;
20921                 tickMap[i] = true;
20922             }
20923         }
20924
20925         this.xTicks.sort(this.DDM.numericSort) ;
20926     },
20927
20928     /**
20929      * Create the array of vertical tick marks if an interval was specified in
20930      * setYConstraint().
20931      * @method setYTicks
20932      * @private
20933      */
20934     setYTicks: function(iStartY, iTickSize) {
20935         this.yTicks = [];
20936         this.yTickSize = iTickSize;
20937
20938         var tickMap = {};
20939
20940         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20941             if (!tickMap[i]) {
20942                 this.yTicks[this.yTicks.length] = i;
20943                 tickMap[i] = true;
20944             }
20945         }
20946
20947         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20948             if (!tickMap[i]) {
20949                 this.yTicks[this.yTicks.length] = i;
20950                 tickMap[i] = true;
20951             }
20952         }
20953
20954         this.yTicks.sort(this.DDM.numericSort) ;
20955     },
20956
20957     /**
20958      * By default, the element can be dragged any place on the screen.  Use
20959      * this method to limit the horizontal travel of the element.  Pass in
20960      * 0,0 for the parameters if you want to lock the drag to the y axis.
20961      * @method setXConstraint
20962      * @param {int} iLeft the number of pixels the element can move to the left
20963      * @param {int} iRight the number of pixels the element can move to the
20964      * right
20965      * @param {int} iTickSize optional parameter for specifying that the
20966      * element
20967      * should move iTickSize pixels at a time.
20968      */
20969     setXConstraint: function(iLeft, iRight, iTickSize) {
20970         this.leftConstraint = iLeft;
20971         this.rightConstraint = iRight;
20972
20973         this.minX = this.initPageX - iLeft;
20974         this.maxX = this.initPageX + iRight;
20975         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20976
20977         this.constrainX = true;
20978     },
20979
20980     /**
20981      * Clears any constraints applied to this instance.  Also clears ticks
20982      * since they can't exist independent of a constraint at this time.
20983      * @method clearConstraints
20984      */
20985     clearConstraints: function() {
20986         this.constrainX = false;
20987         this.constrainY = false;
20988         this.clearTicks();
20989     },
20990
20991     /**
20992      * Clears any tick interval defined for this instance
20993      * @method clearTicks
20994      */
20995     clearTicks: function() {
20996         this.xTicks = null;
20997         this.yTicks = null;
20998         this.xTickSize = 0;
20999         this.yTickSize = 0;
21000     },
21001
21002     /**
21003      * By default, the element can be dragged any place on the screen.  Set
21004      * this to limit the vertical travel of the element.  Pass in 0,0 for the
21005      * parameters if you want to lock the drag to the x axis.
21006      * @method setYConstraint
21007      * @param {int} iUp the number of pixels the element can move up
21008      * @param {int} iDown the number of pixels the element can move down
21009      * @param {int} iTickSize optional parameter for specifying that the
21010      * element should move iTickSize pixels at a time.
21011      */
21012     setYConstraint: function(iUp, iDown, iTickSize) {
21013         this.topConstraint = iUp;
21014         this.bottomConstraint = iDown;
21015
21016         this.minY = this.initPageY - iUp;
21017         this.maxY = this.initPageY + iDown;
21018         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21019
21020         this.constrainY = true;
21021
21022     },
21023
21024     /**
21025      * resetConstraints must be called if you manually reposition a dd element.
21026      * @method resetConstraints
21027      * @param {boolean} maintainOffset
21028      */
21029     resetConstraints: function() {
21030
21031
21032         // Maintain offsets if necessary
21033         if (this.initPageX || this.initPageX === 0) {
21034             // figure out how much this thing has moved
21035             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21036             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21037
21038             this.setInitPosition(dx, dy);
21039
21040         // This is the first time we have detected the element's position
21041         } else {
21042             this.setInitPosition();
21043         }
21044
21045         if (this.constrainX) {
21046             this.setXConstraint( this.leftConstraint,
21047                                  this.rightConstraint,
21048                                  this.xTickSize        );
21049         }
21050
21051         if (this.constrainY) {
21052             this.setYConstraint( this.topConstraint,
21053                                  this.bottomConstraint,
21054                                  this.yTickSize         );
21055         }
21056     },
21057
21058     /**
21059      * Normally the drag element is moved pixel by pixel, but we can specify
21060      * that it move a number of pixels at a time.  This method resolves the
21061      * location when we have it set up like this.
21062      * @method getTick
21063      * @param {int} val where we want to place the object
21064      * @param {int[]} tickArray sorted array of valid points
21065      * @return {int} the closest tick
21066      * @private
21067      */
21068     getTick: function(val, tickArray) {
21069
21070         if (!tickArray) {
21071             // If tick interval is not defined, it is effectively 1 pixel,
21072             // so we return the value passed to us.
21073             return val;
21074         } else if (tickArray[0] >= val) {
21075             // The value is lower than the first tick, so we return the first
21076             // tick.
21077             return tickArray[0];
21078         } else {
21079             for (var i=0, len=tickArray.length; i<len; ++i) {
21080                 var next = i + 1;
21081                 if (tickArray[next] && tickArray[next] >= val) {
21082                     var diff1 = val - tickArray[i];
21083                     var diff2 = tickArray[next] - val;
21084                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21085                 }
21086             }
21087
21088             // The value is larger than the last tick, so we return the last
21089             // tick.
21090             return tickArray[tickArray.length - 1];
21091         }
21092     },
21093
21094     /**
21095      * toString method
21096      * @method toString
21097      * @return {string} string representation of the dd obj
21098      */
21099     toString: function() {
21100         return ("DragDrop " + this.id);
21101     }
21102
21103 });
21104
21105 })();
21106 /*
21107  * Based on:
21108  * Ext JS Library 1.1.1
21109  * Copyright(c) 2006-2007, Ext JS, LLC.
21110  *
21111  * Originally Released Under LGPL - original licence link has changed is not relivant.
21112  *
21113  * Fork - LGPL
21114  * <script type="text/javascript">
21115  */
21116
21117
21118 /**
21119  * The drag and drop utility provides a framework for building drag and drop
21120  * applications.  In addition to enabling drag and drop for specific elements,
21121  * the drag and drop elements are tracked by the manager class, and the
21122  * interactions between the various elements are tracked during the drag and
21123  * the implementing code is notified about these important moments.
21124  */
21125
21126 // Only load the library once.  Rewriting the manager class would orphan
21127 // existing drag and drop instances.
21128 if (!Roo.dd.DragDropMgr) {
21129
21130 /**
21131  * @class Roo.dd.DragDropMgr
21132  * DragDropMgr is a singleton that tracks the element interaction for
21133  * all DragDrop items in the window.  Generally, you will not call
21134  * this class directly, but it does have helper methods that could
21135  * be useful in your DragDrop implementations.
21136  * @static
21137  */
21138 Roo.dd.DragDropMgr = function() {
21139
21140     var Event = Roo.EventManager;
21141
21142     return {
21143
21144         /**
21145          * Two dimensional Array of registered DragDrop objects.  The first
21146          * dimension is the DragDrop item group, the second the DragDrop
21147          * object.
21148          * @property ids
21149          * @type {string: string}
21150          * @private
21151          * @static
21152          */
21153         ids: {},
21154
21155         /**
21156          * Array of element ids defined as drag handles.  Used to determine
21157          * if the element that generated the mousedown event is actually the
21158          * handle and not the html element itself.
21159          * @property handleIds
21160          * @type {string: string}
21161          * @private
21162          * @static
21163          */
21164         handleIds: {},
21165
21166         /**
21167          * the DragDrop object that is currently being dragged
21168          * @property dragCurrent
21169          * @type DragDrop
21170          * @private
21171          * @static
21172          **/
21173         dragCurrent: null,
21174
21175         /**
21176          * the DragDrop object(s) that are being hovered over
21177          * @property dragOvers
21178          * @type Array
21179          * @private
21180          * @static
21181          */
21182         dragOvers: {},
21183
21184         /**
21185          * the X distance between the cursor and the object being dragged
21186          * @property deltaX
21187          * @type int
21188          * @private
21189          * @static
21190          */
21191         deltaX: 0,
21192
21193         /**
21194          * the Y distance between the cursor and the object being dragged
21195          * @property deltaY
21196          * @type int
21197          * @private
21198          * @static
21199          */
21200         deltaY: 0,
21201
21202         /**
21203          * Flag to determine if we should prevent the default behavior of the
21204          * events we define. By default this is true, but this can be set to
21205          * false if you need the default behavior (not recommended)
21206          * @property preventDefault
21207          * @type boolean
21208          * @static
21209          */
21210         preventDefault: true,
21211
21212         /**
21213          * Flag to determine if we should stop the propagation of the events
21214          * we generate. This is true by default but you may want to set it to
21215          * false if the html element contains other features that require the
21216          * mouse click.
21217          * @property stopPropagation
21218          * @type boolean
21219          * @static
21220          */
21221         stopPropagation: true,
21222
21223         /**
21224          * Internal flag that is set to true when drag and drop has been
21225          * intialized
21226          * @property initialized
21227          * @private
21228          * @static
21229          */
21230         initalized: false,
21231
21232         /**
21233          * All drag and drop can be disabled.
21234          * @property locked
21235          * @private
21236          * @static
21237          */
21238         locked: false,
21239
21240         /**
21241          * Called the first time an element is registered.
21242          * @method init
21243          * @private
21244          * @static
21245          */
21246         init: function() {
21247             this.initialized = true;
21248         },
21249
21250         /**
21251          * In point mode, drag and drop interaction is defined by the
21252          * location of the cursor during the drag/drop
21253          * @property POINT
21254          * @type int
21255          * @static
21256          */
21257         POINT: 0,
21258
21259         /**
21260          * In intersect mode, drag and drop interactio nis defined by the
21261          * overlap of two or more drag and drop objects.
21262          * @property INTERSECT
21263          * @type int
21264          * @static
21265          */
21266         INTERSECT: 1,
21267
21268         /**
21269          * The current drag and drop mode.  Default: POINT
21270          * @property mode
21271          * @type int
21272          * @static
21273          */
21274         mode: 0,
21275
21276         /**
21277          * Runs method on all drag and drop objects
21278          * @method _execOnAll
21279          * @private
21280          * @static
21281          */
21282         _execOnAll: function(sMethod, args) {
21283             for (var i in this.ids) {
21284                 for (var j in this.ids[i]) {
21285                     var oDD = this.ids[i][j];
21286                     if (! this.isTypeOfDD(oDD)) {
21287                         continue;
21288                     }
21289                     oDD[sMethod].apply(oDD, args);
21290                 }
21291             }
21292         },
21293
21294         /**
21295          * Drag and drop initialization.  Sets up the global event handlers
21296          * @method _onLoad
21297          * @private
21298          * @static
21299          */
21300         _onLoad: function() {
21301
21302             this.init();
21303
21304             if (!Roo.isTouch) {
21305                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21306                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21307             }
21308             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21309             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21310             
21311             Event.on(window,   "unload",    this._onUnload, this, true);
21312             Event.on(window,   "resize",    this._onResize, this, true);
21313             // Event.on(window,   "mouseout",    this._test);
21314
21315         },
21316
21317         /**
21318          * Reset constraints on all drag and drop objs
21319          * @method _onResize
21320          * @private
21321          * @static
21322          */
21323         _onResize: function(e) {
21324             this._execOnAll("resetConstraints", []);
21325         },
21326
21327         /**
21328          * Lock all drag and drop functionality
21329          * @method lock
21330          * @static
21331          */
21332         lock: function() { this.locked = true; },
21333
21334         /**
21335          * Unlock all drag and drop functionality
21336          * @method unlock
21337          * @static
21338          */
21339         unlock: function() { this.locked = false; },
21340
21341         /**
21342          * Is drag and drop locked?
21343          * @method isLocked
21344          * @return {boolean} True if drag and drop is locked, false otherwise.
21345          * @static
21346          */
21347         isLocked: function() { return this.locked; },
21348
21349         /**
21350          * Location cache that is set for all drag drop objects when a drag is
21351          * initiated, cleared when the drag is finished.
21352          * @property locationCache
21353          * @private
21354          * @static
21355          */
21356         locationCache: {},
21357
21358         /**
21359          * Set useCache to false if you want to force object the lookup of each
21360          * drag and drop linked element constantly during a drag.
21361          * @property useCache
21362          * @type boolean
21363          * @static
21364          */
21365         useCache: true,
21366
21367         /**
21368          * The number of pixels that the mouse needs to move after the
21369          * mousedown before the drag is initiated.  Default=3;
21370          * @property clickPixelThresh
21371          * @type int
21372          * @static
21373          */
21374         clickPixelThresh: 3,
21375
21376         /**
21377          * The number of milliseconds after the mousedown event to initiate the
21378          * drag if we don't get a mouseup event. Default=1000
21379          * @property clickTimeThresh
21380          * @type int
21381          * @static
21382          */
21383         clickTimeThresh: 350,
21384
21385         /**
21386          * Flag that indicates that either the drag pixel threshold or the
21387          * mousdown time threshold has been met
21388          * @property dragThreshMet
21389          * @type boolean
21390          * @private
21391          * @static
21392          */
21393         dragThreshMet: false,
21394
21395         /**
21396          * Timeout used for the click time threshold
21397          * @property clickTimeout
21398          * @type Object
21399          * @private
21400          * @static
21401          */
21402         clickTimeout: null,
21403
21404         /**
21405          * The X position of the mousedown event stored for later use when a
21406          * drag threshold is met.
21407          * @property startX
21408          * @type int
21409          * @private
21410          * @static
21411          */
21412         startX: 0,
21413
21414         /**
21415          * The Y position of the mousedown event stored for later use when a
21416          * drag threshold is met.
21417          * @property startY
21418          * @type int
21419          * @private
21420          * @static
21421          */
21422         startY: 0,
21423
21424         /**
21425          * Each DragDrop instance must be registered with the DragDropMgr.
21426          * This is executed in DragDrop.init()
21427          * @method regDragDrop
21428          * @param {DragDrop} oDD the DragDrop object to register
21429          * @param {String} sGroup the name of the group this element belongs to
21430          * @static
21431          */
21432         regDragDrop: function(oDD, sGroup) {
21433             if (!this.initialized) { this.init(); }
21434
21435             if (!this.ids[sGroup]) {
21436                 this.ids[sGroup] = {};
21437             }
21438             this.ids[sGroup][oDD.id] = oDD;
21439         },
21440
21441         /**
21442          * Removes the supplied dd instance from the supplied group. Executed
21443          * by DragDrop.removeFromGroup, so don't call this function directly.
21444          * @method removeDDFromGroup
21445          * @private
21446          * @static
21447          */
21448         removeDDFromGroup: function(oDD, sGroup) {
21449             if (!this.ids[sGroup]) {
21450                 this.ids[sGroup] = {};
21451             }
21452
21453             var obj = this.ids[sGroup];
21454             if (obj && obj[oDD.id]) {
21455                 delete obj[oDD.id];
21456             }
21457         },
21458
21459         /**
21460          * Unregisters a drag and drop item.  This is executed in
21461          * DragDrop.unreg, use that method instead of calling this directly.
21462          * @method _remove
21463          * @private
21464          * @static
21465          */
21466         _remove: function(oDD) {
21467             for (var g in oDD.groups) {
21468                 if (g && this.ids[g][oDD.id]) {
21469                     delete this.ids[g][oDD.id];
21470                 }
21471             }
21472             delete this.handleIds[oDD.id];
21473         },
21474
21475         /**
21476          * Each DragDrop handle element must be registered.  This is done
21477          * automatically when executing DragDrop.setHandleElId()
21478          * @method regHandle
21479          * @param {String} sDDId the DragDrop id this element is a handle for
21480          * @param {String} sHandleId the id of the element that is the drag
21481          * handle
21482          * @static
21483          */
21484         regHandle: function(sDDId, sHandleId) {
21485             if (!this.handleIds[sDDId]) {
21486                 this.handleIds[sDDId] = {};
21487             }
21488             this.handleIds[sDDId][sHandleId] = sHandleId;
21489         },
21490
21491         /**
21492          * Utility function to determine if a given element has been
21493          * registered as a drag drop item.
21494          * @method isDragDrop
21495          * @param {String} id the element id to check
21496          * @return {boolean} true if this element is a DragDrop item,
21497          * false otherwise
21498          * @static
21499          */
21500         isDragDrop: function(id) {
21501             return ( this.getDDById(id) ) ? true : false;
21502         },
21503
21504         /**
21505          * Returns the drag and drop instances that are in all groups the
21506          * passed in instance belongs to.
21507          * @method getRelated
21508          * @param {DragDrop} p_oDD the obj to get related data for
21509          * @param {boolean} bTargetsOnly if true, only return targetable objs
21510          * @return {DragDrop[]} the related instances
21511          * @static
21512          */
21513         getRelated: function(p_oDD, bTargetsOnly) {
21514             var oDDs = [];
21515             for (var i in p_oDD.groups) {
21516                 for (j in this.ids[i]) {
21517                     var dd = this.ids[i][j];
21518                     if (! this.isTypeOfDD(dd)) {
21519                         continue;
21520                     }
21521                     if (!bTargetsOnly || dd.isTarget) {
21522                         oDDs[oDDs.length] = dd;
21523                     }
21524                 }
21525             }
21526
21527             return oDDs;
21528         },
21529
21530         /**
21531          * Returns true if the specified dd target is a legal target for
21532          * the specifice drag obj
21533          * @method isLegalTarget
21534          * @param {DragDrop} the drag obj
21535          * @param {DragDrop} the target
21536          * @return {boolean} true if the target is a legal target for the
21537          * dd obj
21538          * @static
21539          */
21540         isLegalTarget: function (oDD, oTargetDD) {
21541             var targets = this.getRelated(oDD, true);
21542             for (var i=0, len=targets.length;i<len;++i) {
21543                 if (targets[i].id == oTargetDD.id) {
21544                     return true;
21545                 }
21546             }
21547
21548             return false;
21549         },
21550
21551         /**
21552          * My goal is to be able to transparently determine if an object is
21553          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21554          * returns "object", oDD.constructor.toString() always returns
21555          * "DragDrop" and not the name of the subclass.  So for now it just
21556          * evaluates a well-known variable in DragDrop.
21557          * @method isTypeOfDD
21558          * @param {Object} the object to evaluate
21559          * @return {boolean} true if typeof oDD = DragDrop
21560          * @static
21561          */
21562         isTypeOfDD: function (oDD) {
21563             return (oDD && oDD.__ygDragDrop);
21564         },
21565
21566         /**
21567          * Utility function to determine if a given element has been
21568          * registered as a drag drop handle for the given Drag Drop object.
21569          * @method isHandle
21570          * @param {String} id the element id to check
21571          * @return {boolean} true if this element is a DragDrop handle, false
21572          * otherwise
21573          * @static
21574          */
21575         isHandle: function(sDDId, sHandleId) {
21576             return ( this.handleIds[sDDId] &&
21577                             this.handleIds[sDDId][sHandleId] );
21578         },
21579
21580         /**
21581          * Returns the DragDrop instance for a given id
21582          * @method getDDById
21583          * @param {String} id the id of the DragDrop object
21584          * @return {DragDrop} the drag drop object, null if it is not found
21585          * @static
21586          */
21587         getDDById: function(id) {
21588             for (var i in this.ids) {
21589                 if (this.ids[i][id]) {
21590                     return this.ids[i][id];
21591                 }
21592             }
21593             return null;
21594         },
21595
21596         /**
21597          * Fired after a registered DragDrop object gets the mousedown event.
21598          * Sets up the events required to track the object being dragged
21599          * @method handleMouseDown
21600          * @param {Event} e the event
21601          * @param oDD the DragDrop object being dragged
21602          * @private
21603          * @static
21604          */
21605         handleMouseDown: function(e, oDD) {
21606             if(Roo.QuickTips){
21607                 Roo.QuickTips.disable();
21608             }
21609             this.currentTarget = e.getTarget();
21610
21611             this.dragCurrent = oDD;
21612
21613             var el = oDD.getEl();
21614
21615             // track start position
21616             this.startX = e.getPageX();
21617             this.startY = e.getPageY();
21618
21619             this.deltaX = this.startX - el.offsetLeft;
21620             this.deltaY = this.startY - el.offsetTop;
21621
21622             this.dragThreshMet = false;
21623
21624             this.clickTimeout = setTimeout(
21625                     function() {
21626                         var DDM = Roo.dd.DDM;
21627                         DDM.startDrag(DDM.startX, DDM.startY);
21628                     },
21629                     this.clickTimeThresh );
21630         },
21631
21632         /**
21633          * Fired when either the drag pixel threshol or the mousedown hold
21634          * time threshold has been met.
21635          * @method startDrag
21636          * @param x {int} the X position of the original mousedown
21637          * @param y {int} the Y position of the original mousedown
21638          * @static
21639          */
21640         startDrag: function(x, y) {
21641             clearTimeout(this.clickTimeout);
21642             if (this.dragCurrent) {
21643                 this.dragCurrent.b4StartDrag(x, y);
21644                 this.dragCurrent.startDrag(x, y);
21645             }
21646             this.dragThreshMet = true;
21647         },
21648
21649         /**
21650          * Internal function to handle the mouseup event.  Will be invoked
21651          * from the context of the document.
21652          * @method handleMouseUp
21653          * @param {Event} e the event
21654          * @private
21655          * @static
21656          */
21657         handleMouseUp: function(e) {
21658
21659             if(Roo.QuickTips){
21660                 Roo.QuickTips.enable();
21661             }
21662             if (! this.dragCurrent) {
21663                 return;
21664             }
21665
21666             clearTimeout(this.clickTimeout);
21667
21668             if (this.dragThreshMet) {
21669                 this.fireEvents(e, true);
21670             } else {
21671             }
21672
21673             this.stopDrag(e);
21674
21675             this.stopEvent(e);
21676         },
21677
21678         /**
21679          * Utility to stop event propagation and event default, if these
21680          * features are turned on.
21681          * @method stopEvent
21682          * @param {Event} e the event as returned by this.getEvent()
21683          * @static
21684          */
21685         stopEvent: function(e){
21686             if(this.stopPropagation) {
21687                 e.stopPropagation();
21688             }
21689
21690             if (this.preventDefault) {
21691                 e.preventDefault();
21692             }
21693         },
21694
21695         /**
21696          * Internal function to clean up event handlers after the drag
21697          * operation is complete
21698          * @method stopDrag
21699          * @param {Event} e the event
21700          * @private
21701          * @static
21702          */
21703         stopDrag: function(e) {
21704             // Fire the drag end event for the item that was dragged
21705             if (this.dragCurrent) {
21706                 if (this.dragThreshMet) {
21707                     this.dragCurrent.b4EndDrag(e);
21708                     this.dragCurrent.endDrag(e);
21709                 }
21710
21711                 this.dragCurrent.onMouseUp(e);
21712             }
21713
21714             this.dragCurrent = null;
21715             this.dragOvers = {};
21716         },
21717
21718         /**
21719          * Internal function to handle the mousemove event.  Will be invoked
21720          * from the context of the html element.
21721          *
21722          * @TODO figure out what we can do about mouse events lost when the
21723          * user drags objects beyond the window boundary.  Currently we can
21724          * detect this in internet explorer by verifying that the mouse is
21725          * down during the mousemove event.  Firefox doesn't give us the
21726          * button state on the mousemove event.
21727          * @method handleMouseMove
21728          * @param {Event} e the event
21729          * @private
21730          * @static
21731          */
21732         handleMouseMove: function(e) {
21733             if (! this.dragCurrent) {
21734                 return true;
21735             }
21736
21737             // var button = e.which || e.button;
21738
21739             // check for IE mouseup outside of page boundary
21740             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21741                 this.stopEvent(e);
21742                 return this.handleMouseUp(e);
21743             }
21744
21745             if (!this.dragThreshMet) {
21746                 var diffX = Math.abs(this.startX - e.getPageX());
21747                 var diffY = Math.abs(this.startY - e.getPageY());
21748                 if (diffX > this.clickPixelThresh ||
21749                             diffY > this.clickPixelThresh) {
21750                     this.startDrag(this.startX, this.startY);
21751                 }
21752             }
21753
21754             if (this.dragThreshMet) {
21755                 this.dragCurrent.b4Drag(e);
21756                 this.dragCurrent.onDrag(e);
21757                 if(!this.dragCurrent.moveOnly){
21758                     this.fireEvents(e, false);
21759                 }
21760             }
21761
21762             this.stopEvent(e);
21763
21764             return true;
21765         },
21766
21767         /**
21768          * Iterates over all of the DragDrop elements to find ones we are
21769          * hovering over or dropping on
21770          * @method fireEvents
21771          * @param {Event} e the event
21772          * @param {boolean} isDrop is this a drop op or a mouseover op?
21773          * @private
21774          * @static
21775          */
21776         fireEvents: function(e, isDrop) {
21777             var dc = this.dragCurrent;
21778
21779             // If the user did the mouse up outside of the window, we could
21780             // get here even though we have ended the drag.
21781             if (!dc || dc.isLocked()) {
21782                 return;
21783             }
21784
21785             var pt = e.getPoint();
21786
21787             // cache the previous dragOver array
21788             var oldOvers = [];
21789
21790             var outEvts   = [];
21791             var overEvts  = [];
21792             var dropEvts  = [];
21793             var enterEvts = [];
21794
21795             // Check to see if the object(s) we were hovering over is no longer
21796             // being hovered over so we can fire the onDragOut event
21797             for (var i in this.dragOvers) {
21798
21799                 var ddo = this.dragOvers[i];
21800
21801                 if (! this.isTypeOfDD(ddo)) {
21802                     continue;
21803                 }
21804
21805                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21806                     outEvts.push( ddo );
21807                 }
21808
21809                 oldOvers[i] = true;
21810                 delete this.dragOvers[i];
21811             }
21812
21813             for (var sGroup in dc.groups) {
21814
21815                 if ("string" != typeof sGroup) {
21816                     continue;
21817                 }
21818
21819                 for (i in this.ids[sGroup]) {
21820                     var oDD = this.ids[sGroup][i];
21821                     if (! this.isTypeOfDD(oDD)) {
21822                         continue;
21823                     }
21824
21825                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21826                         if (this.isOverTarget(pt, oDD, this.mode)) {
21827                             // look for drop interactions
21828                             if (isDrop) {
21829                                 dropEvts.push( oDD );
21830                             // look for drag enter and drag over interactions
21831                             } else {
21832
21833                                 // initial drag over: dragEnter fires
21834                                 if (!oldOvers[oDD.id]) {
21835                                     enterEvts.push( oDD );
21836                                 // subsequent drag overs: dragOver fires
21837                                 } else {
21838                                     overEvts.push( oDD );
21839                                 }
21840
21841                                 this.dragOvers[oDD.id] = oDD;
21842                             }
21843                         }
21844                     }
21845                 }
21846             }
21847
21848             if (this.mode) {
21849                 if (outEvts.length) {
21850                     dc.b4DragOut(e, outEvts);
21851                     dc.onDragOut(e, outEvts);
21852                 }
21853
21854                 if (enterEvts.length) {
21855                     dc.onDragEnter(e, enterEvts);
21856                 }
21857
21858                 if (overEvts.length) {
21859                     dc.b4DragOver(e, overEvts);
21860                     dc.onDragOver(e, overEvts);
21861                 }
21862
21863                 if (dropEvts.length) {
21864                     dc.b4DragDrop(e, dropEvts);
21865                     dc.onDragDrop(e, dropEvts);
21866                 }
21867
21868             } else {
21869                 // fire dragout events
21870                 var len = 0;
21871                 for (i=0, len=outEvts.length; i<len; ++i) {
21872                     dc.b4DragOut(e, outEvts[i].id);
21873                     dc.onDragOut(e, outEvts[i].id);
21874                 }
21875
21876                 // fire enter events
21877                 for (i=0,len=enterEvts.length; i<len; ++i) {
21878                     // dc.b4DragEnter(e, oDD.id);
21879                     dc.onDragEnter(e, enterEvts[i].id);
21880                 }
21881
21882                 // fire over events
21883                 for (i=0,len=overEvts.length; i<len; ++i) {
21884                     dc.b4DragOver(e, overEvts[i].id);
21885                     dc.onDragOver(e, overEvts[i].id);
21886                 }
21887
21888                 // fire drop events
21889                 for (i=0, len=dropEvts.length; i<len; ++i) {
21890                     dc.b4DragDrop(e, dropEvts[i].id);
21891                     dc.onDragDrop(e, dropEvts[i].id);
21892                 }
21893
21894             }
21895
21896             // notify about a drop that did not find a target
21897             if (isDrop && !dropEvts.length) {
21898                 dc.onInvalidDrop(e);
21899             }
21900
21901         },
21902
21903         /**
21904          * Helper function for getting the best match from the list of drag
21905          * and drop objects returned by the drag and drop events when we are
21906          * in INTERSECT mode.  It returns either the first object that the
21907          * cursor is over, or the object that has the greatest overlap with
21908          * the dragged element.
21909          * @method getBestMatch
21910          * @param  {DragDrop[]} dds The array of drag and drop objects
21911          * targeted
21912          * @return {DragDrop}       The best single match
21913          * @static
21914          */
21915         getBestMatch: function(dds) {
21916             var winner = null;
21917             // Return null if the input is not what we expect
21918             //if (!dds || !dds.length || dds.length == 0) {
21919                // winner = null;
21920             // If there is only one item, it wins
21921             //} else if (dds.length == 1) {
21922
21923             var len = dds.length;
21924
21925             if (len == 1) {
21926                 winner = dds[0];
21927             } else {
21928                 // Loop through the targeted items
21929                 for (var i=0; i<len; ++i) {
21930                     var dd = dds[i];
21931                     // If the cursor is over the object, it wins.  If the
21932                     // cursor is over multiple matches, the first one we come
21933                     // to wins.
21934                     if (dd.cursorIsOver) {
21935                         winner = dd;
21936                         break;
21937                     // Otherwise the object with the most overlap wins
21938                     } else {
21939                         if (!winner ||
21940                             winner.overlap.getArea() < dd.overlap.getArea()) {
21941                             winner = dd;
21942                         }
21943                     }
21944                 }
21945             }
21946
21947             return winner;
21948         },
21949
21950         /**
21951          * Refreshes the cache of the top-left and bottom-right points of the
21952          * drag and drop objects in the specified group(s).  This is in the
21953          * format that is stored in the drag and drop instance, so typical
21954          * usage is:
21955          * <code>
21956          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21957          * </code>
21958          * Alternatively:
21959          * <code>
21960          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21961          * </code>
21962          * @TODO this really should be an indexed array.  Alternatively this
21963          * method could accept both.
21964          * @method refreshCache
21965          * @param {Object} groups an associative array of groups to refresh
21966          * @static
21967          */
21968         refreshCache: function(groups) {
21969             for (var sGroup in groups) {
21970                 if ("string" != typeof sGroup) {
21971                     continue;
21972                 }
21973                 for (var i in this.ids[sGroup]) {
21974                     var oDD = this.ids[sGroup][i];
21975
21976                     if (this.isTypeOfDD(oDD)) {
21977                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21978                         var loc = this.getLocation(oDD);
21979                         if (loc) {
21980                             this.locationCache[oDD.id] = loc;
21981                         } else {
21982                             delete this.locationCache[oDD.id];
21983                             // this will unregister the drag and drop object if
21984                             // the element is not in a usable state
21985                             // oDD.unreg();
21986                         }
21987                     }
21988                 }
21989             }
21990         },
21991
21992         /**
21993          * This checks to make sure an element exists and is in the DOM.  The
21994          * main purpose is to handle cases where innerHTML is used to remove
21995          * drag and drop objects from the DOM.  IE provides an 'unspecified
21996          * error' when trying to access the offsetParent of such an element
21997          * @method verifyEl
21998          * @param {HTMLElement} el the element to check
21999          * @return {boolean} true if the element looks usable
22000          * @static
22001          */
22002         verifyEl: function(el) {
22003             if (el) {
22004                 var parent;
22005                 if(Roo.isIE){
22006                     try{
22007                         parent = el.offsetParent;
22008                     }catch(e){}
22009                 }else{
22010                     parent = el.offsetParent;
22011                 }
22012                 if (parent) {
22013                     return true;
22014                 }
22015             }
22016
22017             return false;
22018         },
22019
22020         /**
22021          * Returns a Region object containing the drag and drop element's position
22022          * and size, including the padding configured for it
22023          * @method getLocation
22024          * @param {DragDrop} oDD the drag and drop object to get the
22025          *                       location for
22026          * @return {Roo.lib.Region} a Region object representing the total area
22027          *                             the element occupies, including any padding
22028          *                             the instance is configured for.
22029          * @static
22030          */
22031         getLocation: function(oDD) {
22032             if (! this.isTypeOfDD(oDD)) {
22033                 return null;
22034             }
22035
22036             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22037
22038             try {
22039                 pos= Roo.lib.Dom.getXY(el);
22040             } catch (e) { }
22041
22042             if (!pos) {
22043                 return null;
22044             }
22045
22046             x1 = pos[0];
22047             x2 = x1 + el.offsetWidth;
22048             y1 = pos[1];
22049             y2 = y1 + el.offsetHeight;
22050
22051             t = y1 - oDD.padding[0];
22052             r = x2 + oDD.padding[1];
22053             b = y2 + oDD.padding[2];
22054             l = x1 - oDD.padding[3];
22055
22056             return new Roo.lib.Region( t, r, b, l );
22057         },
22058
22059         /**
22060          * Checks the cursor location to see if it over the target
22061          * @method isOverTarget
22062          * @param {Roo.lib.Point} pt The point to evaluate
22063          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22064          * @return {boolean} true if the mouse is over the target
22065          * @private
22066          * @static
22067          */
22068         isOverTarget: function(pt, oTarget, intersect) {
22069             // use cache if available
22070             var loc = this.locationCache[oTarget.id];
22071             if (!loc || !this.useCache) {
22072                 loc = this.getLocation(oTarget);
22073                 this.locationCache[oTarget.id] = loc;
22074
22075             }
22076
22077             if (!loc) {
22078                 return false;
22079             }
22080
22081             oTarget.cursorIsOver = loc.contains( pt );
22082
22083             // DragDrop is using this as a sanity check for the initial mousedown
22084             // in this case we are done.  In POINT mode, if the drag obj has no
22085             // contraints, we are also done. Otherwise we need to evaluate the
22086             // location of the target as related to the actual location of the
22087             // dragged element.
22088             var dc = this.dragCurrent;
22089             if (!dc || !dc.getTargetCoord ||
22090                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22091                 return oTarget.cursorIsOver;
22092             }
22093
22094             oTarget.overlap = null;
22095
22096             // Get the current location of the drag element, this is the
22097             // location of the mouse event less the delta that represents
22098             // where the original mousedown happened on the element.  We
22099             // need to consider constraints and ticks as well.
22100             var pos = dc.getTargetCoord(pt.x, pt.y);
22101
22102             var el = dc.getDragEl();
22103             var curRegion = new Roo.lib.Region( pos.y,
22104                                                    pos.x + el.offsetWidth,
22105                                                    pos.y + el.offsetHeight,
22106                                                    pos.x );
22107
22108             var overlap = curRegion.intersect(loc);
22109
22110             if (overlap) {
22111                 oTarget.overlap = overlap;
22112                 return (intersect) ? true : oTarget.cursorIsOver;
22113             } else {
22114                 return false;
22115             }
22116         },
22117
22118         /**
22119          * unload event handler
22120          * @method _onUnload
22121          * @private
22122          * @static
22123          */
22124         _onUnload: function(e, me) {
22125             Roo.dd.DragDropMgr.unregAll();
22126         },
22127
22128         /**
22129          * Cleans up the drag and drop events and objects.
22130          * @method unregAll
22131          * @private
22132          * @static
22133          */
22134         unregAll: function() {
22135
22136             if (this.dragCurrent) {
22137                 this.stopDrag();
22138                 this.dragCurrent = null;
22139             }
22140
22141             this._execOnAll("unreg", []);
22142
22143             for (i in this.elementCache) {
22144                 delete this.elementCache[i];
22145             }
22146
22147             this.elementCache = {};
22148             this.ids = {};
22149         },
22150
22151         /**
22152          * A cache of DOM elements
22153          * @property elementCache
22154          * @private
22155          * @static
22156          */
22157         elementCache: {},
22158
22159         /**
22160          * Get the wrapper for the DOM element specified
22161          * @method getElWrapper
22162          * @param {String} id the id of the element to get
22163          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22164          * @private
22165          * @deprecated This wrapper isn't that useful
22166          * @static
22167          */
22168         getElWrapper: function(id) {
22169             var oWrapper = this.elementCache[id];
22170             if (!oWrapper || !oWrapper.el) {
22171                 oWrapper = this.elementCache[id] =
22172                     new this.ElementWrapper(Roo.getDom(id));
22173             }
22174             return oWrapper;
22175         },
22176
22177         /**
22178          * Returns the actual DOM element
22179          * @method getElement
22180          * @param {String} id the id of the elment to get
22181          * @return {Object} The element
22182          * @deprecated use Roo.getDom instead
22183          * @static
22184          */
22185         getElement: function(id) {
22186             return Roo.getDom(id);
22187         },
22188
22189         /**
22190          * Returns the style property for the DOM element (i.e.,
22191          * document.getElById(id).style)
22192          * @method getCss
22193          * @param {String} id the id of the elment to get
22194          * @return {Object} The style property of the element
22195          * @deprecated use Roo.getDom instead
22196          * @static
22197          */
22198         getCss: function(id) {
22199             var el = Roo.getDom(id);
22200             return (el) ? el.style : null;
22201         },
22202
22203         /**
22204          * Inner class for cached elements
22205          * @class DragDropMgr.ElementWrapper
22206          * @for DragDropMgr
22207          * @private
22208          * @deprecated
22209          */
22210         ElementWrapper: function(el) {
22211                 /**
22212                  * The element
22213                  * @property el
22214                  */
22215                 this.el = el || null;
22216                 /**
22217                  * The element id
22218                  * @property id
22219                  */
22220                 this.id = this.el && el.id;
22221                 /**
22222                  * A reference to the style property
22223                  * @property css
22224                  */
22225                 this.css = this.el && el.style;
22226             },
22227
22228         /**
22229          * Returns the X position of an html element
22230          * @method getPosX
22231          * @param el the element for which to get the position
22232          * @return {int} the X coordinate
22233          * @for DragDropMgr
22234          * @deprecated use Roo.lib.Dom.getX instead
22235          * @static
22236          */
22237         getPosX: function(el) {
22238             return Roo.lib.Dom.getX(el);
22239         },
22240
22241         /**
22242          * Returns the Y position of an html element
22243          * @method getPosY
22244          * @param el the element for which to get the position
22245          * @return {int} the Y coordinate
22246          * @deprecated use Roo.lib.Dom.getY instead
22247          * @static
22248          */
22249         getPosY: function(el) {
22250             return Roo.lib.Dom.getY(el);
22251         },
22252
22253         /**
22254          * Swap two nodes.  In IE, we use the native method, for others we
22255          * emulate the IE behavior
22256          * @method swapNode
22257          * @param n1 the first node to swap
22258          * @param n2 the other node to swap
22259          * @static
22260          */
22261         swapNode: function(n1, n2) {
22262             if (n1.swapNode) {
22263                 n1.swapNode(n2);
22264             } else {
22265                 var p = n2.parentNode;
22266                 var s = n2.nextSibling;
22267
22268                 if (s == n1) {
22269                     p.insertBefore(n1, n2);
22270                 } else if (n2 == n1.nextSibling) {
22271                     p.insertBefore(n2, n1);
22272                 } else {
22273                     n1.parentNode.replaceChild(n2, n1);
22274                     p.insertBefore(n1, s);
22275                 }
22276             }
22277         },
22278
22279         /**
22280          * Returns the current scroll position
22281          * @method getScroll
22282          * @private
22283          * @static
22284          */
22285         getScroll: function () {
22286             var t, l, dde=document.documentElement, db=document.body;
22287             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22288                 t = dde.scrollTop;
22289                 l = dde.scrollLeft;
22290             } else if (db) {
22291                 t = db.scrollTop;
22292                 l = db.scrollLeft;
22293             } else {
22294
22295             }
22296             return { top: t, left: l };
22297         },
22298
22299         /**
22300          * Returns the specified element style property
22301          * @method getStyle
22302          * @param {HTMLElement} el          the element
22303          * @param {string}      styleProp   the style property
22304          * @return {string} The value of the style property
22305          * @deprecated use Roo.lib.Dom.getStyle
22306          * @static
22307          */
22308         getStyle: function(el, styleProp) {
22309             return Roo.fly(el).getStyle(styleProp);
22310         },
22311
22312         /**
22313          * Gets the scrollTop
22314          * @method getScrollTop
22315          * @return {int} the document's scrollTop
22316          * @static
22317          */
22318         getScrollTop: function () { return this.getScroll().top; },
22319
22320         /**
22321          * Gets the scrollLeft
22322          * @method getScrollLeft
22323          * @return {int} the document's scrollTop
22324          * @static
22325          */
22326         getScrollLeft: function () { return this.getScroll().left; },
22327
22328         /**
22329          * Sets the x/y position of an element to the location of the
22330          * target element.
22331          * @method moveToEl
22332          * @param {HTMLElement} moveEl      The element to move
22333          * @param {HTMLElement} targetEl    The position reference element
22334          * @static
22335          */
22336         moveToEl: function (moveEl, targetEl) {
22337             var aCoord = Roo.lib.Dom.getXY(targetEl);
22338             Roo.lib.Dom.setXY(moveEl, aCoord);
22339         },
22340
22341         /**
22342          * Numeric array sort function
22343          * @method numericSort
22344          * @static
22345          */
22346         numericSort: function(a, b) { return (a - b); },
22347
22348         /**
22349          * Internal counter
22350          * @property _timeoutCount
22351          * @private
22352          * @static
22353          */
22354         _timeoutCount: 0,
22355
22356         /**
22357          * Trying to make the load order less important.  Without this we get
22358          * an error if this file is loaded before the Event Utility.
22359          * @method _addListeners
22360          * @private
22361          * @static
22362          */
22363         _addListeners: function() {
22364             var DDM = Roo.dd.DDM;
22365             if ( Roo.lib.Event && document ) {
22366                 DDM._onLoad();
22367             } else {
22368                 if (DDM._timeoutCount > 2000) {
22369                 } else {
22370                     setTimeout(DDM._addListeners, 10);
22371                     if (document && document.body) {
22372                         DDM._timeoutCount += 1;
22373                     }
22374                 }
22375             }
22376         },
22377
22378         /**
22379          * Recursively searches the immediate parent and all child nodes for
22380          * the handle element in order to determine wheter or not it was
22381          * clicked.
22382          * @method handleWasClicked
22383          * @param node the html element to inspect
22384          * @static
22385          */
22386         handleWasClicked: function(node, id) {
22387             if (this.isHandle(id, node.id)) {
22388                 return true;
22389             } else {
22390                 // check to see if this is a text node child of the one we want
22391                 var p = node.parentNode;
22392
22393                 while (p) {
22394                     if (this.isHandle(id, p.id)) {
22395                         return true;
22396                     } else {
22397                         p = p.parentNode;
22398                     }
22399                 }
22400             }
22401
22402             return false;
22403         }
22404
22405     };
22406
22407 }();
22408
22409 // shorter alias, save a few bytes
22410 Roo.dd.DDM = Roo.dd.DragDropMgr;
22411 Roo.dd.DDM._addListeners();
22412
22413 }/*
22414  * Based on:
22415  * Ext JS Library 1.1.1
22416  * Copyright(c) 2006-2007, Ext JS, LLC.
22417  *
22418  * Originally Released Under LGPL - original licence link has changed is not relivant.
22419  *
22420  * Fork - LGPL
22421  * <script type="text/javascript">
22422  */
22423
22424 /**
22425  * @class Roo.dd.DD
22426  * A DragDrop implementation where the linked element follows the
22427  * mouse cursor during a drag.
22428  * @extends Roo.dd.DragDrop
22429  * @constructor
22430  * @param {String} id the id of the linked element
22431  * @param {String} sGroup the group of related DragDrop items
22432  * @param {object} config an object containing configurable attributes
22433  *                Valid properties for DD:
22434  *                    scroll
22435  */
22436 Roo.dd.DD = function(id, sGroup, config) {
22437     if (id) {
22438         this.init(id, sGroup, config);
22439     }
22440 };
22441
22442 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22443
22444     /**
22445      * When set to true, the utility automatically tries to scroll the browser
22446      * window wehn a drag and drop element is dragged near the viewport boundary.
22447      * Defaults to true.
22448      * @property scroll
22449      * @type boolean
22450      */
22451     scroll: true,
22452
22453     /**
22454      * Sets the pointer offset to the distance between the linked element's top
22455      * left corner and the location the element was clicked
22456      * @method autoOffset
22457      * @param {int} iPageX the X coordinate of the click
22458      * @param {int} iPageY the Y coordinate of the click
22459      */
22460     autoOffset: function(iPageX, iPageY) {
22461         var x = iPageX - this.startPageX;
22462         var y = iPageY - this.startPageY;
22463         this.setDelta(x, y);
22464     },
22465
22466     /**
22467      * Sets the pointer offset.  You can call this directly to force the
22468      * offset to be in a particular location (e.g., pass in 0,0 to set it
22469      * to the center of the object)
22470      * @method setDelta
22471      * @param {int} iDeltaX the distance from the left
22472      * @param {int} iDeltaY the distance from the top
22473      */
22474     setDelta: function(iDeltaX, iDeltaY) {
22475         this.deltaX = iDeltaX;
22476         this.deltaY = iDeltaY;
22477     },
22478
22479     /**
22480      * Sets the drag element to the location of the mousedown or click event,
22481      * maintaining the cursor location relative to the location on the element
22482      * that was clicked.  Override this if you want to place the element in a
22483      * location other than where the cursor is.
22484      * @method setDragElPos
22485      * @param {int} iPageX the X coordinate of the mousedown or drag event
22486      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22487      */
22488     setDragElPos: function(iPageX, iPageY) {
22489         // the first time we do this, we are going to check to make sure
22490         // the element has css positioning
22491
22492         var el = this.getDragEl();
22493         this.alignElWithMouse(el, iPageX, iPageY);
22494     },
22495
22496     /**
22497      * Sets the element to the location of the mousedown or click event,
22498      * maintaining the cursor location relative to the location on the element
22499      * that was clicked.  Override this if you want to place the element in a
22500      * location other than where the cursor is.
22501      * @method alignElWithMouse
22502      * @param {HTMLElement} el the element to move
22503      * @param {int} iPageX the X coordinate of the mousedown or drag event
22504      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22505      */
22506     alignElWithMouse: function(el, iPageX, iPageY) {
22507         var oCoord = this.getTargetCoord(iPageX, iPageY);
22508         var fly = el.dom ? el : Roo.fly(el);
22509         if (!this.deltaSetXY) {
22510             var aCoord = [oCoord.x, oCoord.y];
22511             fly.setXY(aCoord);
22512             var newLeft = fly.getLeft(true);
22513             var newTop  = fly.getTop(true);
22514             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22515         } else {
22516             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22517         }
22518
22519         this.cachePosition(oCoord.x, oCoord.y);
22520         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22521         return oCoord;
22522     },
22523
22524     /**
22525      * Saves the most recent position so that we can reset the constraints and
22526      * tick marks on-demand.  We need to know this so that we can calculate the
22527      * number of pixels the element is offset from its original position.
22528      * @method cachePosition
22529      * @param iPageX the current x position (optional, this just makes it so we
22530      * don't have to look it up again)
22531      * @param iPageY the current y position (optional, this just makes it so we
22532      * don't have to look it up again)
22533      */
22534     cachePosition: function(iPageX, iPageY) {
22535         if (iPageX) {
22536             this.lastPageX = iPageX;
22537             this.lastPageY = iPageY;
22538         } else {
22539             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22540             this.lastPageX = aCoord[0];
22541             this.lastPageY = aCoord[1];
22542         }
22543     },
22544
22545     /**
22546      * Auto-scroll the window if the dragged object has been moved beyond the
22547      * visible window boundary.
22548      * @method autoScroll
22549      * @param {int} x the drag element's x position
22550      * @param {int} y the drag element's y position
22551      * @param {int} h the height of the drag element
22552      * @param {int} w the width of the drag element
22553      * @private
22554      */
22555     autoScroll: function(x, y, h, w) {
22556
22557         if (this.scroll) {
22558             // The client height
22559             var clientH = Roo.lib.Dom.getViewWidth();
22560
22561             // The client width
22562             var clientW = Roo.lib.Dom.getViewHeight();
22563
22564             // The amt scrolled down
22565             var st = this.DDM.getScrollTop();
22566
22567             // The amt scrolled right
22568             var sl = this.DDM.getScrollLeft();
22569
22570             // Location of the bottom of the element
22571             var bot = h + y;
22572
22573             // Location of the right of the element
22574             var right = w + x;
22575
22576             // The distance from the cursor to the bottom of the visible area,
22577             // adjusted so that we don't scroll if the cursor is beyond the
22578             // element drag constraints
22579             var toBot = (clientH + st - y - this.deltaY);
22580
22581             // The distance from the cursor to the right of the visible area
22582             var toRight = (clientW + sl - x - this.deltaX);
22583
22584
22585             // How close to the edge the cursor must be before we scroll
22586             // var thresh = (document.all) ? 100 : 40;
22587             var thresh = 40;
22588
22589             // How many pixels to scroll per autoscroll op.  This helps to reduce
22590             // clunky scrolling. IE is more sensitive about this ... it needs this
22591             // value to be higher.
22592             var scrAmt = (document.all) ? 80 : 30;
22593
22594             // Scroll down if we are near the bottom of the visible page and the
22595             // obj extends below the crease
22596             if ( bot > clientH && toBot < thresh ) {
22597                 window.scrollTo(sl, st + scrAmt);
22598             }
22599
22600             // Scroll up if the window is scrolled down and the top of the object
22601             // goes above the top border
22602             if ( y < st && st > 0 && y - st < thresh ) {
22603                 window.scrollTo(sl, st - scrAmt);
22604             }
22605
22606             // Scroll right if the obj is beyond the right border and the cursor is
22607             // near the border.
22608             if ( right > clientW && toRight < thresh ) {
22609                 window.scrollTo(sl + scrAmt, st);
22610             }
22611
22612             // Scroll left if the window has been scrolled to the right and the obj
22613             // extends past the left border
22614             if ( x < sl && sl > 0 && x - sl < thresh ) {
22615                 window.scrollTo(sl - scrAmt, st);
22616             }
22617         }
22618     },
22619
22620     /**
22621      * Finds the location the element should be placed if we want to move
22622      * it to where the mouse location less the click offset would place us.
22623      * @method getTargetCoord
22624      * @param {int} iPageX the X coordinate of the click
22625      * @param {int} iPageY the Y coordinate of the click
22626      * @return an object that contains the coordinates (Object.x and Object.y)
22627      * @private
22628      */
22629     getTargetCoord: function(iPageX, iPageY) {
22630
22631
22632         var x = iPageX - this.deltaX;
22633         var y = iPageY - this.deltaY;
22634
22635         if (this.constrainX) {
22636             if (x < this.minX) { x = this.minX; }
22637             if (x > this.maxX) { x = this.maxX; }
22638         }
22639
22640         if (this.constrainY) {
22641             if (y < this.minY) { y = this.minY; }
22642             if (y > this.maxY) { y = this.maxY; }
22643         }
22644
22645         x = this.getTick(x, this.xTicks);
22646         y = this.getTick(y, this.yTicks);
22647
22648
22649         return {x:x, y:y};
22650     },
22651
22652     /*
22653      * Sets up config options specific to this class. Overrides
22654      * Roo.dd.DragDrop, but all versions of this method through the
22655      * inheritance chain are called
22656      */
22657     applyConfig: function() {
22658         Roo.dd.DD.superclass.applyConfig.call(this);
22659         this.scroll = (this.config.scroll !== false);
22660     },
22661
22662     /*
22663      * Event that fires prior to the onMouseDown event.  Overrides
22664      * Roo.dd.DragDrop.
22665      */
22666     b4MouseDown: function(e) {
22667         // this.resetConstraints();
22668         this.autoOffset(e.getPageX(),
22669                             e.getPageY());
22670     },
22671
22672     /*
22673      * Event that fires prior to the onDrag event.  Overrides
22674      * Roo.dd.DragDrop.
22675      */
22676     b4Drag: function(e) {
22677         this.setDragElPos(e.getPageX(),
22678                             e.getPageY());
22679     },
22680
22681     toString: function() {
22682         return ("DD " + this.id);
22683     }
22684
22685     //////////////////////////////////////////////////////////////////////////
22686     // Debugging ygDragDrop events that can be overridden
22687     //////////////////////////////////////////////////////////////////////////
22688     /*
22689     startDrag: function(x, y) {
22690     },
22691
22692     onDrag: function(e) {
22693     },
22694
22695     onDragEnter: function(e, id) {
22696     },
22697
22698     onDragOver: function(e, id) {
22699     },
22700
22701     onDragOut: function(e, id) {
22702     },
22703
22704     onDragDrop: function(e, id) {
22705     },
22706
22707     endDrag: function(e) {
22708     }
22709
22710     */
22711
22712 });/*
22713  * Based on:
22714  * Ext JS Library 1.1.1
22715  * Copyright(c) 2006-2007, Ext JS, LLC.
22716  *
22717  * Originally Released Under LGPL - original licence link has changed is not relivant.
22718  *
22719  * Fork - LGPL
22720  * <script type="text/javascript">
22721  */
22722
22723 /**
22724  * @class Roo.dd.DDProxy
22725  * A DragDrop implementation that inserts an empty, bordered div into
22726  * the document that follows the cursor during drag operations.  At the time of
22727  * the click, the frame div is resized to the dimensions of the linked html
22728  * element, and moved to the exact location of the linked element.
22729  *
22730  * References to the "frame" element refer to the single proxy element that
22731  * was created to be dragged in place of all DDProxy elements on the
22732  * page.
22733  *
22734  * @extends Roo.dd.DD
22735  * @constructor
22736  * @param {String} id the id of the linked html element
22737  * @param {String} sGroup the group of related DragDrop objects
22738  * @param {object} config an object containing configurable attributes
22739  *                Valid properties for DDProxy in addition to those in DragDrop:
22740  *                   resizeFrame, centerFrame, dragElId
22741  */
22742 Roo.dd.DDProxy = function(id, sGroup, config) {
22743     if (id) {
22744         this.init(id, sGroup, config);
22745         this.initFrame();
22746     }
22747 };
22748
22749 /**
22750  * The default drag frame div id
22751  * @property Roo.dd.DDProxy.dragElId
22752  * @type String
22753  * @static
22754  */
22755 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22756
22757 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22758
22759     /**
22760      * By default we resize the drag frame to be the same size as the element
22761      * we want to drag (this is to get the frame effect).  We can turn it off
22762      * if we want a different behavior.
22763      * @property resizeFrame
22764      * @type boolean
22765      */
22766     resizeFrame: true,
22767
22768     /**
22769      * By default the frame is positioned exactly where the drag element is, so
22770      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22771      * you do not have constraints on the obj is to have the drag frame centered
22772      * around the cursor.  Set centerFrame to true for this effect.
22773      * @property centerFrame
22774      * @type boolean
22775      */
22776     centerFrame: false,
22777
22778     /**
22779      * Creates the proxy element if it does not yet exist
22780      * @method createFrame
22781      */
22782     createFrame: function() {
22783         var self = this;
22784         var body = document.body;
22785
22786         if (!body || !body.firstChild) {
22787             setTimeout( function() { self.createFrame(); }, 50 );
22788             return;
22789         }
22790
22791         var div = this.getDragEl();
22792
22793         if (!div) {
22794             div    = document.createElement("div");
22795             div.id = this.dragElId;
22796             var s  = div.style;
22797
22798             s.position   = "absolute";
22799             s.visibility = "hidden";
22800             s.cursor     = "move";
22801             s.border     = "2px solid #aaa";
22802             s.zIndex     = 999;
22803
22804             // appendChild can blow up IE if invoked prior to the window load event
22805             // while rendering a table.  It is possible there are other scenarios
22806             // that would cause this to happen as well.
22807             body.insertBefore(div, body.firstChild);
22808         }
22809     },
22810
22811     /**
22812      * Initialization for the drag frame element.  Must be called in the
22813      * constructor of all subclasses
22814      * @method initFrame
22815      */
22816     initFrame: function() {
22817         this.createFrame();
22818     },
22819
22820     applyConfig: function() {
22821         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22822
22823         this.resizeFrame = (this.config.resizeFrame !== false);
22824         this.centerFrame = (this.config.centerFrame);
22825         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22826     },
22827
22828     /**
22829      * Resizes the drag frame to the dimensions of the clicked object, positions
22830      * it over the object, and finally displays it
22831      * @method showFrame
22832      * @param {int} iPageX X click position
22833      * @param {int} iPageY Y click position
22834      * @private
22835      */
22836     showFrame: function(iPageX, iPageY) {
22837         var el = this.getEl();
22838         var dragEl = this.getDragEl();
22839         var s = dragEl.style;
22840
22841         this._resizeProxy();
22842
22843         if (this.centerFrame) {
22844             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22845                            Math.round(parseInt(s.height, 10)/2) );
22846         }
22847
22848         this.setDragElPos(iPageX, iPageY);
22849
22850         Roo.fly(dragEl).show();
22851     },
22852
22853     /**
22854      * The proxy is automatically resized to the dimensions of the linked
22855      * element when a drag is initiated, unless resizeFrame is set to false
22856      * @method _resizeProxy
22857      * @private
22858      */
22859     _resizeProxy: function() {
22860         if (this.resizeFrame) {
22861             var el = this.getEl();
22862             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22863         }
22864     },
22865
22866     // overrides Roo.dd.DragDrop
22867     b4MouseDown: function(e) {
22868         var x = e.getPageX();
22869         var y = e.getPageY();
22870         this.autoOffset(x, y);
22871         this.setDragElPos(x, y);
22872     },
22873
22874     // overrides Roo.dd.DragDrop
22875     b4StartDrag: function(x, y) {
22876         // show the drag frame
22877         this.showFrame(x, y);
22878     },
22879
22880     // overrides Roo.dd.DragDrop
22881     b4EndDrag: function(e) {
22882         Roo.fly(this.getDragEl()).hide();
22883     },
22884
22885     // overrides Roo.dd.DragDrop
22886     // By default we try to move the element to the last location of the frame.
22887     // This is so that the default behavior mirrors that of Roo.dd.DD.
22888     endDrag: function(e) {
22889
22890         var lel = this.getEl();
22891         var del = this.getDragEl();
22892
22893         // Show the drag frame briefly so we can get its position
22894         del.style.visibility = "";
22895
22896         this.beforeMove();
22897         // Hide the linked element before the move to get around a Safari
22898         // rendering bug.
22899         lel.style.visibility = "hidden";
22900         Roo.dd.DDM.moveToEl(lel, del);
22901         del.style.visibility = "hidden";
22902         lel.style.visibility = "";
22903
22904         this.afterDrag();
22905     },
22906
22907     beforeMove : function(){
22908
22909     },
22910
22911     afterDrag : function(){
22912
22913     },
22914
22915     toString: function() {
22916         return ("DDProxy " + this.id);
22917     }
22918
22919 });
22920 /*
22921  * Based on:
22922  * Ext JS Library 1.1.1
22923  * Copyright(c) 2006-2007, Ext JS, LLC.
22924  *
22925  * Originally Released Under LGPL - original licence link has changed is not relivant.
22926  *
22927  * Fork - LGPL
22928  * <script type="text/javascript">
22929  */
22930
22931  /**
22932  * @class Roo.dd.DDTarget
22933  * A DragDrop implementation that does not move, but can be a drop
22934  * target.  You would get the same result by simply omitting implementation
22935  * for the event callbacks, but this way we reduce the processing cost of the
22936  * event listener and the callbacks.
22937  * @extends Roo.dd.DragDrop
22938  * @constructor
22939  * @param {String} id the id of the element that is a drop target
22940  * @param {String} sGroup the group of related DragDrop objects
22941  * @param {object} config an object containing configurable attributes
22942  *                 Valid properties for DDTarget in addition to those in
22943  *                 DragDrop:
22944  *                    none
22945  */
22946 Roo.dd.DDTarget = function(id, sGroup, config) {
22947     if (id) {
22948         this.initTarget(id, sGroup, config);
22949     }
22950     if (config && (config.listeners || config.events)) { 
22951         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22952             listeners : config.listeners || {}, 
22953             events : config.events || {} 
22954         });    
22955     }
22956 };
22957
22958 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22959 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22960     toString: function() {
22961         return ("DDTarget " + this.id);
22962     }
22963 });
22964 /*
22965  * Based on:
22966  * Ext JS Library 1.1.1
22967  * Copyright(c) 2006-2007, Ext JS, LLC.
22968  *
22969  * Originally Released Under LGPL - original licence link has changed is not relivant.
22970  *
22971  * Fork - LGPL
22972  * <script type="text/javascript">
22973  */
22974  
22975
22976 /**
22977  * @class Roo.dd.ScrollManager
22978  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22979  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22980  * @static
22981  */
22982 Roo.dd.ScrollManager = function(){
22983     var ddm = Roo.dd.DragDropMgr;
22984     var els = {};
22985     var dragEl = null;
22986     var proc = {};
22987     
22988     
22989     
22990     var onStop = function(e){
22991         dragEl = null;
22992         clearProc();
22993     };
22994     
22995     var triggerRefresh = function(){
22996         if(ddm.dragCurrent){
22997              ddm.refreshCache(ddm.dragCurrent.groups);
22998         }
22999     };
23000     
23001     var doScroll = function(){
23002         if(ddm.dragCurrent){
23003             var dds = Roo.dd.ScrollManager;
23004             if(!dds.animate){
23005                 if(proc.el.scroll(proc.dir, dds.increment)){
23006                     triggerRefresh();
23007                 }
23008             }else{
23009                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
23010             }
23011         }
23012     };
23013     
23014     var clearProc = function(){
23015         if(proc.id){
23016             clearInterval(proc.id);
23017         }
23018         proc.id = 0;
23019         proc.el = null;
23020         proc.dir = "";
23021     };
23022     
23023     var startProc = function(el, dir){
23024          Roo.log('scroll startproc');
23025         clearProc();
23026         proc.el = el;
23027         proc.dir = dir;
23028         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23029     };
23030     
23031     var onFire = function(e, isDrop){
23032        
23033         if(isDrop || !ddm.dragCurrent){ return; }
23034         var dds = Roo.dd.ScrollManager;
23035         if(!dragEl || dragEl != ddm.dragCurrent){
23036             dragEl = ddm.dragCurrent;
23037             // refresh regions on drag start
23038             dds.refreshCache();
23039         }
23040         
23041         var xy = Roo.lib.Event.getXY(e);
23042         var pt = new Roo.lib.Point(xy[0], xy[1]);
23043         for(var id in els){
23044             var el = els[id], r = el._region;
23045             if(r && r.contains(pt) && el.isScrollable()){
23046                 if(r.bottom - pt.y <= dds.thresh){
23047                     if(proc.el != el){
23048                         startProc(el, "down");
23049                     }
23050                     return;
23051                 }else if(r.right - pt.x <= dds.thresh){
23052                     if(proc.el != el){
23053                         startProc(el, "left");
23054                     }
23055                     return;
23056                 }else if(pt.y - r.top <= dds.thresh){
23057                     if(proc.el != el){
23058                         startProc(el, "up");
23059                     }
23060                     return;
23061                 }else if(pt.x - r.left <= dds.thresh){
23062                     if(proc.el != el){
23063                         startProc(el, "right");
23064                     }
23065                     return;
23066                 }
23067             }
23068         }
23069         clearProc();
23070     };
23071     
23072     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23073     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23074     
23075     return {
23076         /**
23077          * Registers new overflow element(s) to auto scroll
23078          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23079          */
23080         register : function(el){
23081             if(el instanceof Array){
23082                 for(var i = 0, len = el.length; i < len; i++) {
23083                         this.register(el[i]);
23084                 }
23085             }else{
23086                 el = Roo.get(el);
23087                 els[el.id] = el;
23088             }
23089             Roo.dd.ScrollManager.els = els;
23090         },
23091         
23092         /**
23093          * Unregisters overflow element(s) so they are no longer scrolled
23094          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23095          */
23096         unregister : function(el){
23097             if(el instanceof Array){
23098                 for(var i = 0, len = el.length; i < len; i++) {
23099                         this.unregister(el[i]);
23100                 }
23101             }else{
23102                 el = Roo.get(el);
23103                 delete els[el.id];
23104             }
23105         },
23106         
23107         /**
23108          * The number of pixels from the edge of a container the pointer needs to be to 
23109          * trigger scrolling (defaults to 25)
23110          * @type Number
23111          */
23112         thresh : 25,
23113         
23114         /**
23115          * The number of pixels to scroll in each scroll increment (defaults to 50)
23116          * @type Number
23117          */
23118         increment : 100,
23119         
23120         /**
23121          * The frequency of scrolls in milliseconds (defaults to 500)
23122          * @type Number
23123          */
23124         frequency : 500,
23125         
23126         /**
23127          * True to animate the scroll (defaults to true)
23128          * @type Boolean
23129          */
23130         animate: true,
23131         
23132         /**
23133          * The animation duration in seconds - 
23134          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23135          * @type Number
23136          */
23137         animDuration: .4,
23138         
23139         /**
23140          * Manually trigger a cache refresh.
23141          */
23142         refreshCache : function(){
23143             for(var id in els){
23144                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23145                     els[id]._region = els[id].getRegion();
23146                 }
23147             }
23148         }
23149     };
23150 }();/*
23151  * Based on:
23152  * Ext JS Library 1.1.1
23153  * Copyright(c) 2006-2007, Ext JS, LLC.
23154  *
23155  * Originally Released Under LGPL - original licence link has changed is not relivant.
23156  *
23157  * Fork - LGPL
23158  * <script type="text/javascript">
23159  */
23160  
23161
23162 /**
23163  * @class Roo.dd.Registry
23164  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23165  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23166  * @static
23167  */
23168 Roo.dd.Registry = function(){
23169     var elements = {}; 
23170     var handles = {}; 
23171     var autoIdSeed = 0;
23172
23173     var getId = function(el, autogen){
23174         if(typeof el == "string"){
23175             return el;
23176         }
23177         var id = el.id;
23178         if(!id && autogen !== false){
23179             id = "roodd-" + (++autoIdSeed);
23180             el.id = id;
23181         }
23182         return id;
23183     };
23184     
23185     return {
23186     /**
23187      * Register a drag drop element
23188      * @param {String|HTMLElement} element The id or DOM node to register
23189      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23190      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23191      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23192      * populated in the data object (if applicable):
23193      * <pre>
23194 Value      Description<br />
23195 ---------  ------------------------------------------<br />
23196 handles    Array of DOM nodes that trigger dragging<br />
23197            for the element being registered<br />
23198 isHandle   True if the element passed in triggers<br />
23199            dragging itself, else false
23200 </pre>
23201      */
23202         register : function(el, data){
23203             data = data || {};
23204             if(typeof el == "string"){
23205                 el = document.getElementById(el);
23206             }
23207             data.ddel = el;
23208             elements[getId(el)] = data;
23209             if(data.isHandle !== false){
23210                 handles[data.ddel.id] = data;
23211             }
23212             if(data.handles){
23213                 var hs = data.handles;
23214                 for(var i = 0, len = hs.length; i < len; i++){
23215                         handles[getId(hs[i])] = data;
23216                 }
23217             }
23218         },
23219
23220     /**
23221      * Unregister a drag drop element
23222      * @param {String|HTMLElement}  element The id or DOM node to unregister
23223      */
23224         unregister : function(el){
23225             var id = getId(el, false);
23226             var data = elements[id];
23227             if(data){
23228                 delete elements[id];
23229                 if(data.handles){
23230                     var hs = data.handles;
23231                     for(var i = 0, len = hs.length; i < len; i++){
23232                         delete handles[getId(hs[i], false)];
23233                     }
23234                 }
23235             }
23236         },
23237
23238     /**
23239      * Returns the handle registered for a DOM Node by id
23240      * @param {String|HTMLElement} id The DOM node or id to look up
23241      * @return {Object} handle The custom handle data
23242      */
23243         getHandle : function(id){
23244             if(typeof id != "string"){ // must be element?
23245                 id = id.id;
23246             }
23247             return handles[id];
23248         },
23249
23250     /**
23251      * Returns the handle that is registered for the DOM node that is the target of the event
23252      * @param {Event} e The event
23253      * @return {Object} handle The custom handle data
23254      */
23255         getHandleFromEvent : function(e){
23256             var t = Roo.lib.Event.getTarget(e);
23257             return t ? handles[t.id] : null;
23258         },
23259
23260     /**
23261      * Returns a custom data object that is registered for a DOM node by id
23262      * @param {String|HTMLElement} id The DOM node or id to look up
23263      * @return {Object} data The custom data
23264      */
23265         getTarget : function(id){
23266             if(typeof id != "string"){ // must be element?
23267                 id = id.id;
23268             }
23269             return elements[id];
23270         },
23271
23272     /**
23273      * Returns a custom data object that is registered for the DOM node that is the target of the event
23274      * @param {Event} e The event
23275      * @return {Object} data The custom data
23276      */
23277         getTargetFromEvent : function(e){
23278             var t = Roo.lib.Event.getTarget(e);
23279             return t ? elements[t.id] || handles[t.id] : null;
23280         }
23281     };
23282 }();/*
23283  * Based on:
23284  * Ext JS Library 1.1.1
23285  * Copyright(c) 2006-2007, Ext JS, LLC.
23286  *
23287  * Originally Released Under LGPL - original licence link has changed is not relivant.
23288  *
23289  * Fork - LGPL
23290  * <script type="text/javascript">
23291  */
23292  
23293
23294 /**
23295  * @class Roo.dd.StatusProxy
23296  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23297  * default drag proxy used by all Roo.dd components.
23298  * @constructor
23299  * @param {Object} config
23300  */
23301 Roo.dd.StatusProxy = function(config){
23302     Roo.apply(this, config);
23303     this.id = this.id || Roo.id();
23304     this.el = new Roo.Layer({
23305         dh: {
23306             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23307                 {tag: "div", cls: "x-dd-drop-icon"},
23308                 {tag: "div", cls: "x-dd-drag-ghost"}
23309             ]
23310         }, 
23311         shadow: !config || config.shadow !== false
23312     });
23313     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23314     this.dropStatus = this.dropNotAllowed;
23315 };
23316
23317 Roo.dd.StatusProxy.prototype = {
23318     /**
23319      * @cfg {String} dropAllowed
23320      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23321      */
23322     dropAllowed : "x-dd-drop-ok",
23323     /**
23324      * @cfg {String} dropNotAllowed
23325      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23326      */
23327     dropNotAllowed : "x-dd-drop-nodrop",
23328
23329     /**
23330      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23331      * over the current target element.
23332      * @param {String} cssClass The css class for the new drop status indicator image
23333      */
23334     setStatus : function(cssClass){
23335         cssClass = cssClass || this.dropNotAllowed;
23336         if(this.dropStatus != cssClass){
23337             this.el.replaceClass(this.dropStatus, cssClass);
23338             this.dropStatus = cssClass;
23339         }
23340     },
23341
23342     /**
23343      * Resets the status indicator to the default dropNotAllowed value
23344      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23345      */
23346     reset : function(clearGhost){
23347         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23348         this.dropStatus = this.dropNotAllowed;
23349         if(clearGhost){
23350             this.ghost.update("");
23351         }
23352     },
23353
23354     /**
23355      * Updates the contents of the ghost element
23356      * @param {String} html The html that will replace the current innerHTML of the ghost element
23357      */
23358     update : function(html){
23359         if(typeof html == "string"){
23360             this.ghost.update(html);
23361         }else{
23362             this.ghost.update("");
23363             html.style.margin = "0";
23364             this.ghost.dom.appendChild(html);
23365         }
23366         // ensure float = none set?? cant remember why though.
23367         var el = this.ghost.dom.firstChild;
23368                 if(el){
23369                         Roo.fly(el).setStyle('float', 'none');
23370                 }
23371     },
23372     
23373     /**
23374      * Returns the underlying proxy {@link Roo.Layer}
23375      * @return {Roo.Layer} el
23376     */
23377     getEl : function(){
23378         return this.el;
23379     },
23380
23381     /**
23382      * Returns the ghost element
23383      * @return {Roo.Element} el
23384      */
23385     getGhost : function(){
23386         return this.ghost;
23387     },
23388
23389     /**
23390      * Hides the proxy
23391      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23392      */
23393     hide : function(clear){
23394         this.el.hide();
23395         if(clear){
23396             this.reset(true);
23397         }
23398     },
23399
23400     /**
23401      * Stops the repair animation if it's currently running
23402      */
23403     stop : function(){
23404         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23405             this.anim.stop();
23406         }
23407     },
23408
23409     /**
23410      * Displays this proxy
23411      */
23412     show : function(){
23413         this.el.show();
23414     },
23415
23416     /**
23417      * Force the Layer to sync its shadow and shim positions to the element
23418      */
23419     sync : function(){
23420         this.el.sync();
23421     },
23422
23423     /**
23424      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23425      * invalid drop operation by the item being dragged.
23426      * @param {Array} xy The XY position of the element ([x, y])
23427      * @param {Function} callback The function to call after the repair is complete
23428      * @param {Object} scope The scope in which to execute the callback
23429      */
23430     repair : function(xy, callback, scope){
23431         this.callback = callback;
23432         this.scope = scope;
23433         if(xy && this.animRepair !== false){
23434             this.el.addClass("x-dd-drag-repair");
23435             this.el.hideUnders(true);
23436             this.anim = this.el.shift({
23437                 duration: this.repairDuration || .5,
23438                 easing: 'easeOut',
23439                 xy: xy,
23440                 stopFx: true,
23441                 callback: this.afterRepair,
23442                 scope: this
23443             });
23444         }else{
23445             this.afterRepair();
23446         }
23447     },
23448
23449     // private
23450     afterRepair : function(){
23451         this.hide(true);
23452         if(typeof this.callback == "function"){
23453             this.callback.call(this.scope || this);
23454         }
23455         this.callback = null;
23456         this.scope = null;
23457     }
23458 };/*
23459  * Based on:
23460  * Ext JS Library 1.1.1
23461  * Copyright(c) 2006-2007, Ext JS, LLC.
23462  *
23463  * Originally Released Under LGPL - original licence link has changed is not relivant.
23464  *
23465  * Fork - LGPL
23466  * <script type="text/javascript">
23467  */
23468
23469 /**
23470  * @class Roo.dd.DragSource
23471  * @extends Roo.dd.DDProxy
23472  * A simple class that provides the basic implementation needed to make any element draggable.
23473  * @constructor
23474  * @param {String/HTMLElement/Element} el The container element
23475  * @param {Object} config
23476  */
23477 Roo.dd.DragSource = function(el, config){
23478     this.el = Roo.get(el);
23479     this.dragData = {};
23480     
23481     Roo.apply(this, config);
23482     
23483     if(!this.proxy){
23484         this.proxy = new Roo.dd.StatusProxy();
23485     }
23486
23487     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23488           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23489     
23490     this.dragging = false;
23491 };
23492
23493 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23494     /**
23495      * @cfg {String} dropAllowed
23496      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23497      */
23498     dropAllowed : "x-dd-drop-ok",
23499     /**
23500      * @cfg {String} dropNotAllowed
23501      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23502      */
23503     dropNotAllowed : "x-dd-drop-nodrop",
23504
23505     /**
23506      * Returns the data object associated with this drag source
23507      * @return {Object} data An object containing arbitrary data
23508      */
23509     getDragData : function(e){
23510         return this.dragData;
23511     },
23512
23513     // private
23514     onDragEnter : function(e, id){
23515         var target = Roo.dd.DragDropMgr.getDDById(id);
23516         this.cachedTarget = target;
23517         if(this.beforeDragEnter(target, e, id) !== false){
23518             if(target.isNotifyTarget){
23519                 var status = target.notifyEnter(this, e, this.dragData);
23520                 this.proxy.setStatus(status);
23521             }else{
23522                 this.proxy.setStatus(this.dropAllowed);
23523             }
23524             
23525             if(this.afterDragEnter){
23526                 /**
23527                  * An empty function by default, but provided so that you can perform a custom action
23528                  * when the dragged item enters the drop target by providing an implementation.
23529                  * @param {Roo.dd.DragDrop} target The drop target
23530                  * @param {Event} e The event object
23531                  * @param {String} id The id of the dragged element
23532                  * @method afterDragEnter
23533                  */
23534                 this.afterDragEnter(target, e, id);
23535             }
23536         }
23537     },
23538
23539     /**
23540      * An empty function by default, but provided so that you can perform a custom action
23541      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23542      * @param {Roo.dd.DragDrop} target The drop target
23543      * @param {Event} e The event object
23544      * @param {String} id The id of the dragged element
23545      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23546      */
23547     beforeDragEnter : function(target, e, id){
23548         return true;
23549     },
23550
23551     // private
23552     alignElWithMouse: function() {
23553         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23554         this.proxy.sync();
23555     },
23556
23557     // private
23558     onDragOver : function(e, id){
23559         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23560         if(this.beforeDragOver(target, e, id) !== false){
23561             if(target.isNotifyTarget){
23562                 var status = target.notifyOver(this, e, this.dragData);
23563                 this.proxy.setStatus(status);
23564             }
23565
23566             if(this.afterDragOver){
23567                 /**
23568                  * An empty function by default, but provided so that you can perform a custom action
23569                  * while the dragged item is over the drop target by providing an implementation.
23570                  * @param {Roo.dd.DragDrop} target The drop target
23571                  * @param {Event} e The event object
23572                  * @param {String} id The id of the dragged element
23573                  * @method afterDragOver
23574                  */
23575                 this.afterDragOver(target, e, id);
23576             }
23577         }
23578     },
23579
23580     /**
23581      * An empty function by default, but provided so that you can perform a custom action
23582      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23583      * @param {Roo.dd.DragDrop} target The drop target
23584      * @param {Event} e The event object
23585      * @param {String} id The id of the dragged element
23586      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23587      */
23588     beforeDragOver : function(target, e, id){
23589         return true;
23590     },
23591
23592     // private
23593     onDragOut : function(e, id){
23594         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23595         if(this.beforeDragOut(target, e, id) !== false){
23596             if(target.isNotifyTarget){
23597                 target.notifyOut(this, e, this.dragData);
23598             }
23599             this.proxy.reset();
23600             if(this.afterDragOut){
23601                 /**
23602                  * An empty function by default, but provided so that you can perform a custom action
23603                  * after the dragged item is dragged out of the target without dropping.
23604                  * @param {Roo.dd.DragDrop} target The drop target
23605                  * @param {Event} e The event object
23606                  * @param {String} id The id of the dragged element
23607                  * @method afterDragOut
23608                  */
23609                 this.afterDragOut(target, e, id);
23610             }
23611         }
23612         this.cachedTarget = null;
23613     },
23614
23615     /**
23616      * An empty function by default, but provided so that you can perform a custom action before the dragged
23617      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23618      * @param {Roo.dd.DragDrop} target The drop target
23619      * @param {Event} e The event object
23620      * @param {String} id The id of the dragged element
23621      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23622      */
23623     beforeDragOut : function(target, e, id){
23624         return true;
23625     },
23626     
23627     // private
23628     onDragDrop : function(e, id){
23629         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23630         if(this.beforeDragDrop(target, e, id) !== false){
23631             if(target.isNotifyTarget){
23632                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23633                     this.onValidDrop(target, e, id);
23634                 }else{
23635                     this.onInvalidDrop(target, e, id);
23636                 }
23637             }else{
23638                 this.onValidDrop(target, e, id);
23639             }
23640             
23641             if(this.afterDragDrop){
23642                 /**
23643                  * An empty function by default, but provided so that you can perform a custom action
23644                  * after a valid drag drop has occurred by providing an implementation.
23645                  * @param {Roo.dd.DragDrop} target The drop target
23646                  * @param {Event} e The event object
23647                  * @param {String} id The id of the dropped element
23648                  * @method afterDragDrop
23649                  */
23650                 this.afterDragDrop(target, e, id);
23651             }
23652         }
23653         delete this.cachedTarget;
23654     },
23655
23656     /**
23657      * An empty function by default, but provided so that you can perform a custom action before the dragged
23658      * item is dropped onto the target and optionally cancel the onDragDrop.
23659      * @param {Roo.dd.DragDrop} target The drop target
23660      * @param {Event} e The event object
23661      * @param {String} id The id of the dragged element
23662      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23663      */
23664     beforeDragDrop : function(target, e, id){
23665         return true;
23666     },
23667
23668     // private
23669     onValidDrop : function(target, e, id){
23670         this.hideProxy();
23671         if(this.afterValidDrop){
23672             /**
23673              * An empty function by default, but provided so that you can perform a custom action
23674              * after a valid drop has occurred by providing an implementation.
23675              * @param {Object} target The target DD 
23676              * @param {Event} e The event object
23677              * @param {String} id The id of the dropped element
23678              * @method afterInvalidDrop
23679              */
23680             this.afterValidDrop(target, e, id);
23681         }
23682     },
23683
23684     // private
23685     getRepairXY : function(e, data){
23686         return this.el.getXY();  
23687     },
23688
23689     // private
23690     onInvalidDrop : function(target, e, id){
23691         this.beforeInvalidDrop(target, e, id);
23692         if(this.cachedTarget){
23693             if(this.cachedTarget.isNotifyTarget){
23694                 this.cachedTarget.notifyOut(this, e, this.dragData);
23695             }
23696             this.cacheTarget = null;
23697         }
23698         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23699
23700         if(this.afterInvalidDrop){
23701             /**
23702              * An empty function by default, but provided so that you can perform a custom action
23703              * after an invalid drop has occurred by providing an implementation.
23704              * @param {Event} e The event object
23705              * @param {String} id The id of the dropped element
23706              * @method afterInvalidDrop
23707              */
23708             this.afterInvalidDrop(e, id);
23709         }
23710     },
23711
23712     // private
23713     afterRepair : function(){
23714         if(Roo.enableFx){
23715             this.el.highlight(this.hlColor || "c3daf9");
23716         }
23717         this.dragging = false;
23718     },
23719
23720     /**
23721      * An empty function by default, but provided so that you can perform a custom action after an invalid
23722      * drop has occurred.
23723      * @param {Roo.dd.DragDrop} target The drop target
23724      * @param {Event} e The event object
23725      * @param {String} id The id of the dragged element
23726      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23727      */
23728     beforeInvalidDrop : function(target, e, id){
23729         return true;
23730     },
23731
23732     // private
23733     handleMouseDown : function(e){
23734         if(this.dragging) {
23735             return;
23736         }
23737         var data = this.getDragData(e);
23738         if(data && this.onBeforeDrag(data, e) !== false){
23739             this.dragData = data;
23740             this.proxy.stop();
23741             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23742         } 
23743     },
23744
23745     /**
23746      * An empty function by default, but provided so that you can perform a custom action before the initial
23747      * drag event begins and optionally cancel it.
23748      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23749      * @param {Event} e The event object
23750      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23751      */
23752     onBeforeDrag : function(data, e){
23753         return true;
23754     },
23755
23756     /**
23757      * An empty function by default, but provided so that you can perform a custom action once the initial
23758      * drag event has begun.  The drag cannot be canceled from this function.
23759      * @param {Number} x The x position of the click on the dragged object
23760      * @param {Number} y The y position of the click on the dragged object
23761      */
23762     onStartDrag : Roo.emptyFn,
23763
23764     // private - YUI override
23765     startDrag : function(x, y){
23766         this.proxy.reset();
23767         this.dragging = true;
23768         this.proxy.update("");
23769         this.onInitDrag(x, y);
23770         this.proxy.show();
23771     },
23772
23773     // private
23774     onInitDrag : function(x, y){
23775         var clone = this.el.dom.cloneNode(true);
23776         clone.id = Roo.id(); // prevent duplicate ids
23777         this.proxy.update(clone);
23778         this.onStartDrag(x, y);
23779         return true;
23780     },
23781
23782     /**
23783      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23784      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23785      */
23786     getProxy : function(){
23787         return this.proxy;  
23788     },
23789
23790     /**
23791      * Hides the drag source's {@link Roo.dd.StatusProxy}
23792      */
23793     hideProxy : function(){
23794         this.proxy.hide();  
23795         this.proxy.reset(true);
23796         this.dragging = false;
23797     },
23798
23799     // private
23800     triggerCacheRefresh : function(){
23801         Roo.dd.DDM.refreshCache(this.groups);
23802     },
23803
23804     // private - override to prevent hiding
23805     b4EndDrag: function(e) {
23806     },
23807
23808     // private - override to prevent moving
23809     endDrag : function(e){
23810         this.onEndDrag(this.dragData, e);
23811     },
23812
23813     // private
23814     onEndDrag : function(data, e){
23815     },
23816     
23817     // private - pin to cursor
23818     autoOffset : function(x, y) {
23819         this.setDelta(-12, -20);
23820     }    
23821 });/*
23822  * Based on:
23823  * Ext JS Library 1.1.1
23824  * Copyright(c) 2006-2007, Ext JS, LLC.
23825  *
23826  * Originally Released Under LGPL - original licence link has changed is not relivant.
23827  *
23828  * Fork - LGPL
23829  * <script type="text/javascript">
23830  */
23831
23832
23833 /**
23834  * @class Roo.dd.DropTarget
23835  * @extends Roo.dd.DDTarget
23836  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23837  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23838  * @constructor
23839  * @param {String/HTMLElement/Element} el The container element
23840  * @param {Object} config
23841  */
23842 Roo.dd.DropTarget = function(el, config){
23843     this.el = Roo.get(el);
23844     
23845     var listeners = false; ;
23846     if (config && config.listeners) {
23847         listeners= config.listeners;
23848         delete config.listeners;
23849     }
23850     Roo.apply(this, config);
23851     
23852     if(this.containerScroll){
23853         Roo.dd.ScrollManager.register(this.el);
23854     }
23855     this.addEvents( {
23856          /**
23857          * @scope Roo.dd.DropTarget
23858          */
23859          
23860          /**
23861          * @event enter
23862          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23863          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23864          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23865          * 
23866          * IMPORTANT : it should set  this.valid to true|false
23867          * 
23868          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23869          * @param {Event} e The event
23870          * @param {Object} data An object containing arbitrary data supplied by the drag source
23871          */
23872         "enter" : true,
23873         
23874          /**
23875          * @event over
23876          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23877          * This method will be called on every mouse movement while the drag source is over the drop target.
23878          * This default implementation simply returns the dropAllowed config value.
23879          * 
23880          * IMPORTANT : it should set  this.valid to true|false
23881          * 
23882          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23883          * @param {Event} e The event
23884          * @param {Object} data An object containing arbitrary data supplied by the drag source
23885          
23886          */
23887         "over" : true,
23888         /**
23889          * @event out
23890          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23891          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23892          * overClass (if any) from the drop element.
23893          * 
23894          * 
23895          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23896          * @param {Event} e The event
23897          * @param {Object} data An object containing arbitrary data supplied by the drag source
23898          */
23899          "out" : true,
23900          
23901         /**
23902          * @event drop
23903          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23904          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23905          * implementation that does something to process the drop event and returns true so that the drag source's
23906          * repair action does not run.
23907          * 
23908          * IMPORTANT : it should set this.success
23909          * 
23910          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23911          * @param {Event} e The event
23912          * @param {Object} data An object containing arbitrary data supplied by the drag source
23913         */
23914          "drop" : true
23915     });
23916             
23917      
23918     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23919         this.el.dom, 
23920         this.ddGroup || this.group,
23921         {
23922             isTarget: true,
23923             listeners : listeners || {} 
23924            
23925         
23926         }
23927     );
23928
23929 };
23930
23931 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23932     /**
23933      * @cfg {String} overClass
23934      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23935      */
23936      /**
23937      * @cfg {String} ddGroup
23938      * The drag drop group to handle drop events for
23939      */
23940      
23941     /**
23942      * @cfg {String} dropAllowed
23943      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23944      */
23945     dropAllowed : "x-dd-drop-ok",
23946     /**
23947      * @cfg {String} dropNotAllowed
23948      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23949      */
23950     dropNotAllowed : "x-dd-drop-nodrop",
23951     /**
23952      * @cfg {boolean} success
23953      * set this after drop listener.. 
23954      */
23955     success : false,
23956     /**
23957      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23958      * if the drop point is valid for over/enter..
23959      */
23960     valid : false,
23961     // private
23962     isTarget : true,
23963
23964     // private
23965     isNotifyTarget : true,
23966     
23967     /**
23968      * @hide
23969      */
23970     notifyEnter : function(dd, e, data)
23971     {
23972         this.valid = true;
23973         this.fireEvent('enter', dd, e, data);
23974         if(this.overClass){
23975             this.el.addClass(this.overClass);
23976         }
23977         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23978             this.valid ? this.dropAllowed : this.dropNotAllowed
23979         );
23980     },
23981
23982     /**
23983      * @hide
23984      */
23985     notifyOver : function(dd, e, data)
23986     {
23987         this.valid = true;
23988         this.fireEvent('over', dd, e, data);
23989         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23990             this.valid ? this.dropAllowed : this.dropNotAllowed
23991         );
23992     },
23993
23994     /**
23995      * @hide
23996      */
23997     notifyOut : function(dd, e, data)
23998     {
23999         this.fireEvent('out', dd, e, data);
24000         if(this.overClass){
24001             this.el.removeClass(this.overClass);
24002         }
24003     },
24004
24005     /**
24006      * @hide
24007      */
24008     notifyDrop : function(dd, e, data)
24009     {
24010         this.success = false;
24011         this.fireEvent('drop', dd, e, data);
24012         return this.success;
24013     }
24014 });/*
24015  * Based on:
24016  * Ext JS Library 1.1.1
24017  * Copyright(c) 2006-2007, Ext JS, LLC.
24018  *
24019  * Originally Released Under LGPL - original licence link has changed is not relivant.
24020  *
24021  * Fork - LGPL
24022  * <script type="text/javascript">
24023  */
24024
24025
24026 /**
24027  * @class Roo.dd.DragZone
24028  * @extends Roo.dd.DragSource
24029  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24030  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24031  * @constructor
24032  * @param {String/HTMLElement/Element} el The container element
24033  * @param {Object} config
24034  */
24035 Roo.dd.DragZone = function(el, config){
24036     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24037     if(this.containerScroll){
24038         Roo.dd.ScrollManager.register(this.el);
24039     }
24040 };
24041
24042 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24043     /**
24044      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24045      * for auto scrolling during drag operations.
24046      */
24047     /**
24048      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24049      * method after a failed drop (defaults to "c3daf9" - light blue)
24050      */
24051
24052     /**
24053      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24054      * for a valid target to drag based on the mouse down. Override this method
24055      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24056      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24057      * @param {EventObject} e The mouse down event
24058      * @return {Object} The dragData
24059      */
24060     getDragData : function(e){
24061         return Roo.dd.Registry.getHandleFromEvent(e);
24062     },
24063     
24064     /**
24065      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24066      * this.dragData.ddel
24067      * @param {Number} x The x position of the click on the dragged object
24068      * @param {Number} y The y position of the click on the dragged object
24069      * @return {Boolean} true to continue the drag, false to cancel
24070      */
24071     onInitDrag : function(x, y){
24072         this.proxy.update(this.dragData.ddel.cloneNode(true));
24073         this.onStartDrag(x, y);
24074         return true;
24075     },
24076     
24077     /**
24078      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24079      */
24080     afterRepair : function(){
24081         if(Roo.enableFx){
24082             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24083         }
24084         this.dragging = false;
24085     },
24086
24087     /**
24088      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24089      * the XY of this.dragData.ddel
24090      * @param {EventObject} e The mouse up event
24091      * @return {Array} The xy location (e.g. [100, 200])
24092      */
24093     getRepairXY : function(e){
24094         return Roo.Element.fly(this.dragData.ddel).getXY();  
24095     }
24096 });/*
24097  * Based on:
24098  * Ext JS Library 1.1.1
24099  * Copyright(c) 2006-2007, Ext JS, LLC.
24100  *
24101  * Originally Released Under LGPL - original licence link has changed is not relivant.
24102  *
24103  * Fork - LGPL
24104  * <script type="text/javascript">
24105  */
24106 /**
24107  * @class Roo.dd.DropZone
24108  * @extends Roo.dd.DropTarget
24109  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24110  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24111  * @constructor
24112  * @param {String/HTMLElement/Element} el The container element
24113  * @param {Object} config
24114  */
24115 Roo.dd.DropZone = function(el, config){
24116     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24117 };
24118
24119 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24120     /**
24121      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24122      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24123      * provide your own custom lookup.
24124      * @param {Event} e The event
24125      * @return {Object} data The custom data
24126      */
24127     getTargetFromEvent : function(e){
24128         return Roo.dd.Registry.getTargetFromEvent(e);
24129     },
24130
24131     /**
24132      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24133      * that it has registered.  This method has no default implementation and should be overridden to provide
24134      * node-specific processing if necessary.
24135      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24136      * {@link #getTargetFromEvent} for this node)
24137      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24138      * @param {Event} e The event
24139      * @param {Object} data An object containing arbitrary data supplied by the drag source
24140      */
24141     onNodeEnter : function(n, dd, e, data){
24142         
24143     },
24144
24145     /**
24146      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24147      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24148      * overridden to provide the proper feedback.
24149      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24150      * {@link #getTargetFromEvent} for this node)
24151      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24152      * @param {Event} e The event
24153      * @param {Object} data An object containing arbitrary data supplied by the drag source
24154      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24155      * underlying {@link Roo.dd.StatusProxy} can be updated
24156      */
24157     onNodeOver : function(n, dd, e, data){
24158         return this.dropAllowed;
24159     },
24160
24161     /**
24162      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24163      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24164      * node-specific processing if necessary.
24165      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24166      * {@link #getTargetFromEvent} for this node)
24167      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24168      * @param {Event} e The event
24169      * @param {Object} data An object containing arbitrary data supplied by the drag source
24170      */
24171     onNodeOut : function(n, dd, e, data){
24172         
24173     },
24174
24175     /**
24176      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24177      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24178      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24179      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24180      * {@link #getTargetFromEvent} for this node)
24181      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24182      * @param {Event} e The event
24183      * @param {Object} data An object containing arbitrary data supplied by the drag source
24184      * @return {Boolean} True if the drop was valid, else false
24185      */
24186     onNodeDrop : function(n, dd, e, data){
24187         return false;
24188     },
24189
24190     /**
24191      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24192      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24193      * it should be overridden to provide the proper feedback if necessary.
24194      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24195      * @param {Event} e The event
24196      * @param {Object} data An object containing arbitrary data supplied by the drag source
24197      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24198      * underlying {@link Roo.dd.StatusProxy} can be updated
24199      */
24200     onContainerOver : function(dd, e, data){
24201         return this.dropNotAllowed;
24202     },
24203
24204     /**
24205      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24206      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24207      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24208      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24209      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24210      * @param {Event} e The event
24211      * @param {Object} data An object containing arbitrary data supplied by the drag source
24212      * @return {Boolean} True if the drop was valid, else false
24213      */
24214     onContainerDrop : function(dd, e, data){
24215         return false;
24216     },
24217
24218     /**
24219      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24220      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24221      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24222      * you should override this method and provide a custom implementation.
24223      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24224      * @param {Event} e The event
24225      * @param {Object} data An object containing arbitrary data supplied by the drag source
24226      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24227      * underlying {@link Roo.dd.StatusProxy} can be updated
24228      */
24229     notifyEnter : function(dd, e, data){
24230         return this.dropNotAllowed;
24231     },
24232
24233     /**
24234      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24235      * This method will be called on every mouse movement while the drag source is over the drop zone.
24236      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24237      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24238      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24239      * registered node, it will call {@link #onContainerOver}.
24240      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24241      * @param {Event} e The event
24242      * @param {Object} data An object containing arbitrary data supplied by the drag source
24243      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24244      * underlying {@link Roo.dd.StatusProxy} can be updated
24245      */
24246     notifyOver : function(dd, e, data){
24247         var n = this.getTargetFromEvent(e);
24248         if(!n){ // not over valid drop target
24249             if(this.lastOverNode){
24250                 this.onNodeOut(this.lastOverNode, dd, e, data);
24251                 this.lastOverNode = null;
24252             }
24253             return this.onContainerOver(dd, e, data);
24254         }
24255         if(this.lastOverNode != n){
24256             if(this.lastOverNode){
24257                 this.onNodeOut(this.lastOverNode, dd, e, data);
24258             }
24259             this.onNodeEnter(n, dd, e, data);
24260             this.lastOverNode = n;
24261         }
24262         return this.onNodeOver(n, dd, e, data);
24263     },
24264
24265     /**
24266      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24267      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24268      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24269      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24270      * @param {Event} e The event
24271      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24272      */
24273     notifyOut : function(dd, e, data){
24274         if(this.lastOverNode){
24275             this.onNodeOut(this.lastOverNode, dd, e, data);
24276             this.lastOverNode = null;
24277         }
24278     },
24279
24280     /**
24281      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24282      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24283      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24284      * otherwise it will call {@link #onContainerDrop}.
24285      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24286      * @param {Event} e The event
24287      * @param {Object} data An object containing arbitrary data supplied by the drag source
24288      * @return {Boolean} True if the drop was valid, else false
24289      */
24290     notifyDrop : function(dd, e, data){
24291         if(this.lastOverNode){
24292             this.onNodeOut(this.lastOverNode, dd, e, data);
24293             this.lastOverNode = null;
24294         }
24295         var n = this.getTargetFromEvent(e);
24296         return n ?
24297             this.onNodeDrop(n, dd, e, data) :
24298             this.onContainerDrop(dd, e, data);
24299     },
24300
24301     // private
24302     triggerCacheRefresh : function(){
24303         Roo.dd.DDM.refreshCache(this.groups);
24304     }  
24305 });/*
24306  * Based on:
24307  * Ext JS Library 1.1.1
24308  * Copyright(c) 2006-2007, Ext JS, LLC.
24309  *
24310  * Originally Released Under LGPL - original licence link has changed is not relivant.
24311  *
24312  * Fork - LGPL
24313  * <script type="text/javascript">
24314  */
24315
24316
24317 /**
24318  * @class Roo.data.SortTypes
24319  * @static
24320  * Defines the default sorting (casting?) comparison functions used when sorting data.
24321  */
24322 Roo.data.SortTypes = {
24323     /**
24324      * Default sort that does nothing
24325      * @param {Mixed} s The value being converted
24326      * @return {Mixed} The comparison value
24327      */
24328     none : function(s){
24329         return s;
24330     },
24331     
24332     /**
24333      * The regular expression used to strip tags
24334      * @type {RegExp}
24335      * @property
24336      */
24337     stripTagsRE : /<\/?[^>]+>/gi,
24338     
24339     /**
24340      * Strips all HTML tags to sort on text only
24341      * @param {Mixed} s The value being converted
24342      * @return {String} The comparison value
24343      */
24344     asText : function(s){
24345         return String(s).replace(this.stripTagsRE, "");
24346     },
24347     
24348     /**
24349      * Strips all HTML tags to sort on text only - Case insensitive
24350      * @param {Mixed} s The value being converted
24351      * @return {String} The comparison value
24352      */
24353     asUCText : function(s){
24354         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24355     },
24356     
24357     /**
24358      * Case insensitive string
24359      * @param {Mixed} s The value being converted
24360      * @return {String} The comparison value
24361      */
24362     asUCString : function(s) {
24363         return String(s).toUpperCase();
24364     },
24365     
24366     /**
24367      * Date sorting
24368      * @param {Mixed} s The value being converted
24369      * @return {Number} The comparison value
24370      */
24371     asDate : function(s) {
24372         if(!s){
24373             return 0;
24374         }
24375         if(s instanceof Date){
24376             return s.getTime();
24377         }
24378         return Date.parse(String(s));
24379     },
24380     
24381     /**
24382      * Float sorting
24383      * @param {Mixed} s The value being converted
24384      * @return {Float} The comparison value
24385      */
24386     asFloat : function(s) {
24387         var val = parseFloat(String(s).replace(/,/g, ""));
24388         if(isNaN(val)) {
24389             val = 0;
24390         }
24391         return val;
24392     },
24393     
24394     /**
24395      * Integer sorting
24396      * @param {Mixed} s The value being converted
24397      * @return {Number} The comparison value
24398      */
24399     asInt : function(s) {
24400         var val = parseInt(String(s).replace(/,/g, ""));
24401         if(isNaN(val)) {
24402             val = 0;
24403         }
24404         return val;
24405     }
24406 };/*
24407  * Based on:
24408  * Ext JS Library 1.1.1
24409  * Copyright(c) 2006-2007, Ext JS, LLC.
24410  *
24411  * Originally Released Under LGPL - original licence link has changed is not relivant.
24412  *
24413  * Fork - LGPL
24414  * <script type="text/javascript">
24415  */
24416
24417 /**
24418 * @class Roo.data.Record
24419  * Instances of this class encapsulate both record <em>definition</em> information, and record
24420  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24421  * to access Records cached in an {@link Roo.data.Store} object.<br>
24422  * <p>
24423  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24424  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24425  * objects.<br>
24426  * <p>
24427  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24428  * @constructor
24429  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24430  * {@link #create}. The parameters are the same.
24431  * @param {Array} data An associative Array of data values keyed by the field name.
24432  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24433  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24434  * not specified an integer id is generated.
24435  */
24436 Roo.data.Record = function(data, id){
24437     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24438     this.data = data;
24439 };
24440
24441 /**
24442  * Generate a constructor for a specific record layout.
24443  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24444  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24445  * Each field definition object may contain the following properties: <ul>
24446  * <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,
24447  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24448  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24449  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24450  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24451  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24452  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24453  * this may be omitted.</p></li>
24454  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24455  * <ul><li>auto (Default, implies no conversion)</li>
24456  * <li>string</li>
24457  * <li>int</li>
24458  * <li>float</li>
24459  * <li>boolean</li>
24460  * <li>date</li></ul></p></li>
24461  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24462  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24463  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24464  * by the Reader into an object that will be stored in the Record. It is passed the
24465  * following parameters:<ul>
24466  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24467  * </ul></p></li>
24468  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24469  * </ul>
24470  * <br>usage:<br><pre><code>
24471 var TopicRecord = Roo.data.Record.create(
24472     {name: 'title', mapping: 'topic_title'},
24473     {name: 'author', mapping: 'username'},
24474     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24475     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24476     {name: 'lastPoster', mapping: 'user2'},
24477     {name: 'excerpt', mapping: 'post_text'}
24478 );
24479
24480 var myNewRecord = new TopicRecord({
24481     title: 'Do my job please',
24482     author: 'noobie',
24483     totalPosts: 1,
24484     lastPost: new Date(),
24485     lastPoster: 'Animal',
24486     excerpt: 'No way dude!'
24487 });
24488 myStore.add(myNewRecord);
24489 </code></pre>
24490  * @method create
24491  * @static
24492  */
24493 Roo.data.Record.create = function(o){
24494     var f = function(){
24495         f.superclass.constructor.apply(this, arguments);
24496     };
24497     Roo.extend(f, Roo.data.Record);
24498     var p = f.prototype;
24499     p.fields = new Roo.util.MixedCollection(false, function(field){
24500         return field.name;
24501     });
24502     for(var i = 0, len = o.length; i < len; i++){
24503         p.fields.add(new Roo.data.Field(o[i]));
24504     }
24505     f.getField = function(name){
24506         return p.fields.get(name);  
24507     };
24508     return f;
24509 };
24510
24511 Roo.data.Record.AUTO_ID = 1000;
24512 Roo.data.Record.EDIT = 'edit';
24513 Roo.data.Record.REJECT = 'reject';
24514 Roo.data.Record.COMMIT = 'commit';
24515
24516 Roo.data.Record.prototype = {
24517     /**
24518      * Readonly flag - true if this record has been modified.
24519      * @type Boolean
24520      */
24521     dirty : false,
24522     editing : false,
24523     error: null,
24524     modified: null,
24525
24526     // private
24527     join : function(store){
24528         this.store = store;
24529     },
24530
24531     /**
24532      * Set the named field to the specified value.
24533      * @param {String} name The name of the field to set.
24534      * @param {Object} value The value to set the field to.
24535      */
24536     set : function(name, value){
24537         if(this.data[name] == value){
24538             return;
24539         }
24540         this.dirty = true;
24541         if(!this.modified){
24542             this.modified = {};
24543         }
24544         if(typeof this.modified[name] == 'undefined'){
24545             this.modified[name] = this.data[name];
24546         }
24547         this.data[name] = value;
24548         if(!this.editing && this.store){
24549             this.store.afterEdit(this);
24550         }       
24551     },
24552
24553     /**
24554      * Get the value of the named field.
24555      * @param {String} name The name of the field to get the value of.
24556      * @return {Object} The value of the field.
24557      */
24558     get : function(name){
24559         return this.data[name]; 
24560     },
24561
24562     // private
24563     beginEdit : function(){
24564         this.editing = true;
24565         this.modified = {}; 
24566     },
24567
24568     // private
24569     cancelEdit : function(){
24570         this.editing = false;
24571         delete this.modified;
24572     },
24573
24574     // private
24575     endEdit : function(){
24576         this.editing = false;
24577         if(this.dirty && this.store){
24578             this.store.afterEdit(this);
24579         }
24580     },
24581
24582     /**
24583      * Usually called by the {@link Roo.data.Store} which owns the Record.
24584      * Rejects all changes made to the Record since either creation, or the last commit operation.
24585      * Modified fields are reverted to their original values.
24586      * <p>
24587      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24588      * of reject operations.
24589      */
24590     reject : function(){
24591         var m = this.modified;
24592         for(var n in m){
24593             if(typeof m[n] != "function"){
24594                 this.data[n] = m[n];
24595             }
24596         }
24597         this.dirty = false;
24598         delete this.modified;
24599         this.editing = false;
24600         if(this.store){
24601             this.store.afterReject(this);
24602         }
24603     },
24604
24605     /**
24606      * Usually called by the {@link Roo.data.Store} which owns the Record.
24607      * Commits all changes made to the Record since either creation, or the last commit operation.
24608      * <p>
24609      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24610      * of commit operations.
24611      */
24612     commit : function(){
24613         this.dirty = false;
24614         delete this.modified;
24615         this.editing = false;
24616         if(this.store){
24617             this.store.afterCommit(this);
24618         }
24619     },
24620
24621     // private
24622     hasError : function(){
24623         return this.error != null;
24624     },
24625
24626     // private
24627     clearError : function(){
24628         this.error = null;
24629     },
24630
24631     /**
24632      * Creates a copy of this record.
24633      * @param {String} id (optional) A new record id if you don't want to use this record's id
24634      * @return {Record}
24635      */
24636     copy : function(newId) {
24637         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24638     }
24639 };/*
24640  * Based on:
24641  * Ext JS Library 1.1.1
24642  * Copyright(c) 2006-2007, Ext JS, LLC.
24643  *
24644  * Originally Released Under LGPL - original licence link has changed is not relivant.
24645  *
24646  * Fork - LGPL
24647  * <script type="text/javascript">
24648  */
24649
24650
24651
24652 /**
24653  * @class Roo.data.Store
24654  * @extends Roo.util.Observable
24655  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24656  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24657  * <p>
24658  * 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
24659  * has no knowledge of the format of the data returned by the Proxy.<br>
24660  * <p>
24661  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24662  * instances from the data object. These records are cached and made available through accessor functions.
24663  * @constructor
24664  * Creates a new Store.
24665  * @param {Object} config A config object containing the objects needed for the Store to access data,
24666  * and read the data into Records.
24667  */
24668 Roo.data.Store = function(config){
24669     this.data = new Roo.util.MixedCollection(false);
24670     this.data.getKey = function(o){
24671         return o.id;
24672     };
24673     this.baseParams = {};
24674     // private
24675     this.paramNames = {
24676         "start" : "start",
24677         "limit" : "limit",
24678         "sort" : "sort",
24679         "dir" : "dir",
24680         "multisort" : "_multisort"
24681     };
24682
24683     if(config && config.data){
24684         this.inlineData = config.data;
24685         delete config.data;
24686     }
24687
24688     Roo.apply(this, config);
24689     
24690     if(this.reader){ // reader passed
24691         this.reader = Roo.factory(this.reader, Roo.data);
24692         this.reader.xmodule = this.xmodule || false;
24693         if(!this.recordType){
24694             this.recordType = this.reader.recordType;
24695         }
24696         if(this.reader.onMetaChange){
24697             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24698         }
24699     }
24700
24701     if(this.recordType){
24702         this.fields = this.recordType.prototype.fields;
24703     }
24704     this.modified = [];
24705
24706     this.addEvents({
24707         /**
24708          * @event datachanged
24709          * Fires when the data cache has changed, and a widget which is using this Store
24710          * as a Record cache should refresh its view.
24711          * @param {Store} this
24712          */
24713         datachanged : true,
24714         /**
24715          * @event metachange
24716          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24717          * @param {Store} this
24718          * @param {Object} meta The JSON metadata
24719          */
24720         metachange : true,
24721         /**
24722          * @event add
24723          * Fires when Records have been added to the Store
24724          * @param {Store} this
24725          * @param {Roo.data.Record[]} records The array of Records added
24726          * @param {Number} index The index at which the record(s) were added
24727          */
24728         add : true,
24729         /**
24730          * @event remove
24731          * Fires when a Record has been removed from the Store
24732          * @param {Store} this
24733          * @param {Roo.data.Record} record The Record that was removed
24734          * @param {Number} index The index at which the record was removed
24735          */
24736         remove : true,
24737         /**
24738          * @event update
24739          * Fires when a Record has been updated
24740          * @param {Store} this
24741          * @param {Roo.data.Record} record The Record that was updated
24742          * @param {String} operation The update operation being performed.  Value may be one of:
24743          * <pre><code>
24744  Roo.data.Record.EDIT
24745  Roo.data.Record.REJECT
24746  Roo.data.Record.COMMIT
24747          * </code></pre>
24748          */
24749         update : true,
24750         /**
24751          * @event clear
24752          * Fires when the data cache has been cleared.
24753          * @param {Store} this
24754          */
24755         clear : true,
24756         /**
24757          * @event beforeload
24758          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24759          * the load action will be canceled.
24760          * @param {Store} this
24761          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24762          */
24763         beforeload : true,
24764         /**
24765          * @event beforeloadadd
24766          * Fires after a new set of Records has been loaded.
24767          * @param {Store} this
24768          * @param {Roo.data.Record[]} records The Records that were loaded
24769          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24770          */
24771         beforeloadadd : true,
24772         /**
24773          * @event load
24774          * Fires after a new set of Records has been loaded, before they are added to the store.
24775          * @param {Store} this
24776          * @param {Roo.data.Record[]} records The Records that were loaded
24777          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24778          * @params {Object} return from reader
24779          */
24780         load : true,
24781         /**
24782          * @event loadexception
24783          * Fires if an exception occurs in the Proxy during loading.
24784          * Called with the signature of the Proxy's "loadexception" event.
24785          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24786          * 
24787          * @param {Proxy} 
24788          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24789          * @param {Object} load options 
24790          * @param {Object} jsonData from your request (normally this contains the Exception)
24791          */
24792         loadexception : true
24793     });
24794     
24795     if(this.proxy){
24796         this.proxy = Roo.factory(this.proxy, Roo.data);
24797         this.proxy.xmodule = this.xmodule || false;
24798         this.relayEvents(this.proxy,  ["loadexception"]);
24799     }
24800     this.sortToggle = {};
24801     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24802
24803     Roo.data.Store.superclass.constructor.call(this);
24804
24805     if(this.inlineData){
24806         this.loadData(this.inlineData);
24807         delete this.inlineData;
24808     }
24809 };
24810
24811 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24812      /**
24813     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24814     * without a remote query - used by combo/forms at present.
24815     */
24816     
24817     /**
24818     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24819     */
24820     /**
24821     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24822     */
24823     /**
24824     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24825     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24826     */
24827     /**
24828     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24829     * on any HTTP request
24830     */
24831     /**
24832     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24833     */
24834     /**
24835     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24836     */
24837     multiSort: false,
24838     /**
24839     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24840     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24841     */
24842     remoteSort : false,
24843
24844     /**
24845     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24846      * loaded or when a record is removed. (defaults to false).
24847     */
24848     pruneModifiedRecords : false,
24849
24850     // private
24851     lastOptions : null,
24852
24853     /**
24854      * Add Records to the Store and fires the add event.
24855      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24856      */
24857     add : function(records){
24858         records = [].concat(records);
24859         for(var i = 0, len = records.length; i < len; i++){
24860             records[i].join(this);
24861         }
24862         var index = this.data.length;
24863         this.data.addAll(records);
24864         this.fireEvent("add", this, records, index);
24865     },
24866
24867     /**
24868      * Remove a Record from the Store and fires the remove event.
24869      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24870      */
24871     remove : function(record){
24872         var index = this.data.indexOf(record);
24873         this.data.removeAt(index);
24874  
24875         if(this.pruneModifiedRecords){
24876             this.modified.remove(record);
24877         }
24878         this.fireEvent("remove", this, record, index);
24879     },
24880
24881     /**
24882      * Remove all Records from the Store and fires the clear event.
24883      */
24884     removeAll : function(){
24885         this.data.clear();
24886         if(this.pruneModifiedRecords){
24887             this.modified = [];
24888         }
24889         this.fireEvent("clear", this);
24890     },
24891
24892     /**
24893      * Inserts Records to the Store at the given index and fires the add event.
24894      * @param {Number} index The start index at which to insert the passed Records.
24895      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24896      */
24897     insert : function(index, records){
24898         records = [].concat(records);
24899         for(var i = 0, len = records.length; i < len; i++){
24900             this.data.insert(index, records[i]);
24901             records[i].join(this);
24902         }
24903         this.fireEvent("add", this, records, index);
24904     },
24905
24906     /**
24907      * Get the index within the cache of the passed Record.
24908      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24909      * @return {Number} The index of the passed Record. Returns -1 if not found.
24910      */
24911     indexOf : function(record){
24912         return this.data.indexOf(record);
24913     },
24914
24915     /**
24916      * Get the index within the cache of the Record with the passed id.
24917      * @param {String} id The id of the Record to find.
24918      * @return {Number} The index of the Record. Returns -1 if not found.
24919      */
24920     indexOfId : function(id){
24921         return this.data.indexOfKey(id);
24922     },
24923
24924     /**
24925      * Get the Record with the specified id.
24926      * @param {String} id The id of the Record to find.
24927      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24928      */
24929     getById : function(id){
24930         return this.data.key(id);
24931     },
24932
24933     /**
24934      * Get the Record at the specified index.
24935      * @param {Number} index The index of the Record to find.
24936      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24937      */
24938     getAt : function(index){
24939         return this.data.itemAt(index);
24940     },
24941
24942     /**
24943      * Returns a range of Records between specified indices.
24944      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24945      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24946      * @return {Roo.data.Record[]} An array of Records
24947      */
24948     getRange : function(start, end){
24949         return this.data.getRange(start, end);
24950     },
24951
24952     // private
24953     storeOptions : function(o){
24954         o = Roo.apply({}, o);
24955         delete o.callback;
24956         delete o.scope;
24957         this.lastOptions = o;
24958     },
24959
24960     /**
24961      * Loads the Record cache from the configured Proxy using the configured Reader.
24962      * <p>
24963      * If using remote paging, then the first load call must specify the <em>start</em>
24964      * and <em>limit</em> properties in the options.params property to establish the initial
24965      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24966      * <p>
24967      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24968      * and this call will return before the new data has been loaded. Perform any post-processing
24969      * in a callback function, or in a "load" event handler.</strong>
24970      * <p>
24971      * @param {Object} options An object containing properties which control loading options:<ul>
24972      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24973      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24974      * <pre>
24975                 {
24976                     data : data,  // array of key=>value data like JsonReader
24977                     total : data.length,
24978                     success : true
24979                     
24980                 }
24981         </pre>
24982             }.</li>
24983      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24984      * passed the following arguments:<ul>
24985      * <li>r : Roo.data.Record[]</li>
24986      * <li>options: Options object from the load call</li>
24987      * <li>success: Boolean success indicator</li></ul></li>
24988      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24989      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24990      * </ul>
24991      */
24992     load : function(options){
24993         options = options || {};
24994         if(this.fireEvent("beforeload", this, options) !== false){
24995             this.storeOptions(options);
24996             var p = Roo.apply(options.params || {}, this.baseParams);
24997             // if meta was not loaded from remote source.. try requesting it.
24998             if (!this.reader.metaFromRemote) {
24999                 p._requestMeta = 1;
25000             }
25001             if(this.sortInfo && this.remoteSort){
25002                 var pn = this.paramNames;
25003                 p[pn["sort"]] = this.sortInfo.field;
25004                 p[pn["dir"]] = this.sortInfo.direction;
25005             }
25006             if (this.multiSort) {
25007                 var pn = this.paramNames;
25008                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
25009             }
25010             
25011             this.proxy.load(p, this.reader, this.loadRecords, this, options);
25012         }
25013     },
25014
25015     /**
25016      * Reloads the Record cache from the configured Proxy using the configured Reader and
25017      * the options from the last load operation performed.
25018      * @param {Object} options (optional) An object containing properties which may override the options
25019      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25020      * the most recently used options are reused).
25021      */
25022     reload : function(options){
25023         this.load(Roo.applyIf(options||{}, this.lastOptions));
25024     },
25025
25026     // private
25027     // Called as a callback by the Reader during a load operation.
25028     loadRecords : function(o, options, success){
25029          
25030         if(!o){
25031             if(success !== false){
25032                 this.fireEvent("load", this, [], options, o);
25033             }
25034             if(options.callback){
25035                 options.callback.call(options.scope || this, [], options, false);
25036             }
25037             return;
25038         }
25039         // if data returned failure - throw an exception.
25040         if (o.success === false) {
25041             // show a message if no listener is registered.
25042             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25043                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25044             }
25045             // loadmask wil be hooked into this..
25046             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25047             return;
25048         }
25049         var r = o.records, t = o.totalRecords || r.length;
25050         
25051         this.fireEvent("beforeloadadd", this, r, options, o);
25052         
25053         if(!options || options.add !== true){
25054             if(this.pruneModifiedRecords){
25055                 this.modified = [];
25056             }
25057             for(var i = 0, len = r.length; i < len; i++){
25058                 r[i].join(this);
25059             }
25060             if(this.snapshot){
25061                 this.data = this.snapshot;
25062                 delete this.snapshot;
25063             }
25064             this.data.clear();
25065             this.data.addAll(r);
25066             this.totalLength = t;
25067             this.applySort();
25068             this.fireEvent("datachanged", this);
25069         }else{
25070             this.totalLength = Math.max(t, this.data.length+r.length);
25071             this.add(r);
25072         }
25073         
25074         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25075                 
25076             var e = new Roo.data.Record({});
25077
25078             e.set(this.parent.displayField, this.parent.emptyTitle);
25079             e.set(this.parent.valueField, '');
25080
25081             this.insert(0, e);
25082         }
25083             
25084         this.fireEvent("load", this, r, options, o);
25085         if(options.callback){
25086             options.callback.call(options.scope || this, r, options, true);
25087         }
25088     },
25089
25090
25091     /**
25092      * Loads data from a passed data block. A Reader which understands the format of the data
25093      * must have been configured in the constructor.
25094      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25095      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25096      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25097      */
25098     loadData : function(o, append){
25099         var r = this.reader.readRecords(o);
25100         this.loadRecords(r, {add: append}, true);
25101     },
25102     
25103      /**
25104      * using 'cn' the nested child reader read the child array into it's child stores.
25105      * @param {Object} rec The record with a 'children array
25106      */
25107     loadDataFromChildren : function(rec)
25108     {
25109         this.loadData(this.reader.toLoadData(rec));
25110     },
25111     
25112
25113     /**
25114      * Gets the number of cached records.
25115      * <p>
25116      * <em>If using paging, this may not be the total size of the dataset. If the data object
25117      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25118      * the data set size</em>
25119      */
25120     getCount : function(){
25121         return this.data.length || 0;
25122     },
25123
25124     /**
25125      * Gets the total number of records in the dataset as returned by the server.
25126      * <p>
25127      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25128      * the dataset size</em>
25129      */
25130     getTotalCount : function(){
25131         return this.totalLength || 0;
25132     },
25133
25134     /**
25135      * Returns the sort state of the Store as an object with two properties:
25136      * <pre><code>
25137  field {String} The name of the field by which the Records are sorted
25138  direction {String} The sort order, "ASC" or "DESC"
25139      * </code></pre>
25140      */
25141     getSortState : function(){
25142         return this.sortInfo;
25143     },
25144
25145     // private
25146     applySort : function(){
25147         if(this.sortInfo && !this.remoteSort){
25148             var s = this.sortInfo, f = s.field;
25149             var st = this.fields.get(f).sortType;
25150             var fn = function(r1, r2){
25151                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25152                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25153             };
25154             this.data.sort(s.direction, fn);
25155             if(this.snapshot && this.snapshot != this.data){
25156                 this.snapshot.sort(s.direction, fn);
25157             }
25158         }
25159     },
25160
25161     /**
25162      * Sets the default sort column and order to be used by the next load operation.
25163      * @param {String} fieldName The name of the field to sort by.
25164      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25165      */
25166     setDefaultSort : function(field, dir){
25167         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25168     },
25169
25170     /**
25171      * Sort the Records.
25172      * If remote sorting is used, the sort is performed on the server, and the cache is
25173      * reloaded. If local sorting is used, the cache is sorted internally.
25174      * @param {String} fieldName The name of the field to sort by.
25175      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25176      */
25177     sort : function(fieldName, dir){
25178         var f = this.fields.get(fieldName);
25179         if(!dir){
25180             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25181             
25182             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25183                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25184             }else{
25185                 dir = f.sortDir;
25186             }
25187         }
25188         this.sortToggle[f.name] = dir;
25189         this.sortInfo = {field: f.name, direction: dir};
25190         if(!this.remoteSort){
25191             this.applySort();
25192             this.fireEvent("datachanged", this);
25193         }else{
25194             this.load(this.lastOptions);
25195         }
25196     },
25197
25198     /**
25199      * Calls the specified function for each of the Records in the cache.
25200      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25201      * Returning <em>false</em> aborts and exits the iteration.
25202      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25203      */
25204     each : function(fn, scope){
25205         this.data.each(fn, scope);
25206     },
25207
25208     /**
25209      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25210      * (e.g., during paging).
25211      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25212      */
25213     getModifiedRecords : function(){
25214         return this.modified;
25215     },
25216
25217     // private
25218     createFilterFn : function(property, value, anyMatch){
25219         if(!value.exec){ // not a regex
25220             value = String(value);
25221             if(value.length == 0){
25222                 return false;
25223             }
25224             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25225         }
25226         return function(r){
25227             return value.test(r.data[property]);
25228         };
25229     },
25230
25231     /**
25232      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25233      * @param {String} property A field on your records
25234      * @param {Number} start The record index to start at (defaults to 0)
25235      * @param {Number} end The last record index to include (defaults to length - 1)
25236      * @return {Number} The sum
25237      */
25238     sum : function(property, start, end){
25239         var rs = this.data.items, v = 0;
25240         start = start || 0;
25241         end = (end || end === 0) ? end : rs.length-1;
25242
25243         for(var i = start; i <= end; i++){
25244             v += (rs[i].data[property] || 0);
25245         }
25246         return v;
25247     },
25248
25249     /**
25250      * Filter the records by a specified property.
25251      * @param {String} field A field on your records
25252      * @param {String/RegExp} value Either a string that the field
25253      * should start with or a RegExp to test against the field
25254      * @param {Boolean} anyMatch True to match any part not just the beginning
25255      */
25256     filter : function(property, value, anyMatch){
25257         var fn = this.createFilterFn(property, value, anyMatch);
25258         return fn ? this.filterBy(fn) : this.clearFilter();
25259     },
25260
25261     /**
25262      * Filter by a function. The specified function will be called with each
25263      * record in this data source. If the function returns true the record is included,
25264      * otherwise it is filtered.
25265      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25266      * @param {Object} scope (optional) The scope of the function (defaults to this)
25267      */
25268     filterBy : function(fn, scope){
25269         this.snapshot = this.snapshot || this.data;
25270         this.data = this.queryBy(fn, scope||this);
25271         this.fireEvent("datachanged", this);
25272     },
25273
25274     /**
25275      * Query the records by a specified property.
25276      * @param {String} field A field on your records
25277      * @param {String/RegExp} value Either a string that the field
25278      * should start with or a RegExp to test against the field
25279      * @param {Boolean} anyMatch True to match any part not just the beginning
25280      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25281      */
25282     query : function(property, value, anyMatch){
25283         var fn = this.createFilterFn(property, value, anyMatch);
25284         return fn ? this.queryBy(fn) : this.data.clone();
25285     },
25286
25287     /**
25288      * Query by a function. The specified function will be called with each
25289      * record in this data source. If the function returns true the record is included
25290      * in the results.
25291      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25292      * @param {Object} scope (optional) The scope of the function (defaults to this)
25293       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25294      **/
25295     queryBy : function(fn, scope){
25296         var data = this.snapshot || this.data;
25297         return data.filterBy(fn, scope||this);
25298     },
25299
25300     /**
25301      * Collects unique values for a particular dataIndex from this store.
25302      * @param {String} dataIndex The property to collect
25303      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25304      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25305      * @return {Array} An array of the unique values
25306      **/
25307     collect : function(dataIndex, allowNull, bypassFilter){
25308         var d = (bypassFilter === true && this.snapshot) ?
25309                 this.snapshot.items : this.data.items;
25310         var v, sv, r = [], l = {};
25311         for(var i = 0, len = d.length; i < len; i++){
25312             v = d[i].data[dataIndex];
25313             sv = String(v);
25314             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25315                 l[sv] = true;
25316                 r[r.length] = v;
25317             }
25318         }
25319         return r;
25320     },
25321
25322     /**
25323      * Revert to a view of the Record cache with no filtering applied.
25324      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25325      */
25326     clearFilter : function(suppressEvent){
25327         if(this.snapshot && this.snapshot != this.data){
25328             this.data = this.snapshot;
25329             delete this.snapshot;
25330             if(suppressEvent !== true){
25331                 this.fireEvent("datachanged", this);
25332             }
25333         }
25334     },
25335
25336     // private
25337     afterEdit : function(record){
25338         if(this.modified.indexOf(record) == -1){
25339             this.modified.push(record);
25340         }
25341         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25342     },
25343     
25344     // private
25345     afterReject : function(record){
25346         this.modified.remove(record);
25347         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25348     },
25349
25350     // private
25351     afterCommit : function(record){
25352         this.modified.remove(record);
25353         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25354     },
25355
25356     /**
25357      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25358      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25359      */
25360     commitChanges : function(){
25361         var m = this.modified.slice(0);
25362         this.modified = [];
25363         for(var i = 0, len = m.length; i < len; i++){
25364             m[i].commit();
25365         }
25366     },
25367
25368     /**
25369      * Cancel outstanding changes on all changed records.
25370      */
25371     rejectChanges : function(){
25372         var m = this.modified.slice(0);
25373         this.modified = [];
25374         for(var i = 0, len = m.length; i < len; i++){
25375             m[i].reject();
25376         }
25377     },
25378
25379     onMetaChange : function(meta, rtype, o){
25380         this.recordType = rtype;
25381         this.fields = rtype.prototype.fields;
25382         delete this.snapshot;
25383         this.sortInfo = meta.sortInfo || this.sortInfo;
25384         this.modified = [];
25385         this.fireEvent('metachange', this, this.reader.meta);
25386     },
25387     
25388     moveIndex : function(data, type)
25389     {
25390         var index = this.indexOf(data);
25391         
25392         var newIndex = index + type;
25393         
25394         this.remove(data);
25395         
25396         this.insert(newIndex, data);
25397         
25398     }
25399 });/*
25400  * Based on:
25401  * Ext JS Library 1.1.1
25402  * Copyright(c) 2006-2007, Ext JS, LLC.
25403  *
25404  * Originally Released Under LGPL - original licence link has changed is not relivant.
25405  *
25406  * Fork - LGPL
25407  * <script type="text/javascript">
25408  */
25409
25410 /**
25411  * @class Roo.data.SimpleStore
25412  * @extends Roo.data.Store
25413  * Small helper class to make creating Stores from Array data easier.
25414  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25415  * @cfg {Array} fields An array of field definition objects, or field name strings.
25416  * @cfg {Object} an existing reader (eg. copied from another store)
25417  * @cfg {Array} data The multi-dimensional array of data
25418  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25419  * @cfg {Roo.data.Reader} reader  [not-required] 
25420  * @constructor
25421  * @param {Object} config
25422  */
25423 Roo.data.SimpleStore = function(config)
25424 {
25425     Roo.data.SimpleStore.superclass.constructor.call(this, {
25426         isLocal : true,
25427         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25428                 id: config.id
25429             },
25430             Roo.data.Record.create(config.fields)
25431         ),
25432         proxy : new Roo.data.MemoryProxy(config.data)
25433     });
25434     this.load();
25435 };
25436 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25437  * Based on:
25438  * Ext JS Library 1.1.1
25439  * Copyright(c) 2006-2007, Ext JS, LLC.
25440  *
25441  * Originally Released Under LGPL - original licence link has changed is not relivant.
25442  *
25443  * Fork - LGPL
25444  * <script type="text/javascript">
25445  */
25446
25447 /**
25448 /**
25449  * @extends Roo.data.Store
25450  * @class Roo.data.JsonStore
25451  * Small helper class to make creating Stores for JSON data easier. <br/>
25452 <pre><code>
25453 var store = new Roo.data.JsonStore({
25454     url: 'get-images.php',
25455     root: 'images',
25456     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25457 });
25458 </code></pre>
25459  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25460  * JsonReader and HttpProxy (unless inline data is provided).</b>
25461  * @cfg {Array} fields An array of field definition objects, or field name strings.
25462  * @constructor
25463  * @param {Object} config
25464  */
25465 Roo.data.JsonStore = function(c){
25466     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25467         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25468         reader: new Roo.data.JsonReader(c, c.fields)
25469     }));
25470 };
25471 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25472  * Based on:
25473  * Ext JS Library 1.1.1
25474  * Copyright(c) 2006-2007, Ext JS, LLC.
25475  *
25476  * Originally Released Under LGPL - original licence link has changed is not relivant.
25477  *
25478  * Fork - LGPL
25479  * <script type="text/javascript">
25480  */
25481
25482  
25483 Roo.data.Field = function(config){
25484     if(typeof config == "string"){
25485         config = {name: config};
25486     }
25487     Roo.apply(this, config);
25488     
25489     if(!this.type){
25490         this.type = "auto";
25491     }
25492     
25493     var st = Roo.data.SortTypes;
25494     // named sortTypes are supported, here we look them up
25495     if(typeof this.sortType == "string"){
25496         this.sortType = st[this.sortType];
25497     }
25498     
25499     // set default sortType for strings and dates
25500     if(!this.sortType){
25501         switch(this.type){
25502             case "string":
25503                 this.sortType = st.asUCString;
25504                 break;
25505             case "date":
25506                 this.sortType = st.asDate;
25507                 break;
25508             default:
25509                 this.sortType = st.none;
25510         }
25511     }
25512
25513     // define once
25514     var stripRe = /[\$,%]/g;
25515
25516     // prebuilt conversion function for this field, instead of
25517     // switching every time we're reading a value
25518     if(!this.convert){
25519         var cv, dateFormat = this.dateFormat;
25520         switch(this.type){
25521             case "":
25522             case "auto":
25523             case undefined:
25524                 cv = function(v){ return v; };
25525                 break;
25526             case "string":
25527                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25528                 break;
25529             case "int":
25530                 cv = function(v){
25531                     return v !== undefined && v !== null && v !== '' ?
25532                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25533                     };
25534                 break;
25535             case "float":
25536                 cv = function(v){
25537                     return v !== undefined && v !== null && v !== '' ?
25538                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25539                     };
25540                 break;
25541             case "bool":
25542             case "boolean":
25543                 cv = function(v){ return v === true || v === "true" || v == 1; };
25544                 break;
25545             case "date":
25546                 cv = function(v){
25547                     if(!v){
25548                         return '';
25549                     }
25550                     if(v instanceof Date){
25551                         return v;
25552                     }
25553                     if(dateFormat){
25554                         if(dateFormat == "timestamp"){
25555                             return new Date(v*1000);
25556                         }
25557                         return Date.parseDate(v, dateFormat);
25558                     }
25559                     var parsed = Date.parse(v);
25560                     return parsed ? new Date(parsed) : null;
25561                 };
25562              break;
25563             
25564         }
25565         this.convert = cv;
25566     }
25567 };
25568
25569 Roo.data.Field.prototype = {
25570     dateFormat: null,
25571     defaultValue: "",
25572     mapping: null,
25573     sortType : null,
25574     sortDir : "ASC"
25575 };/*
25576  * Based on:
25577  * Ext JS Library 1.1.1
25578  * Copyright(c) 2006-2007, Ext JS, LLC.
25579  *
25580  * Originally Released Under LGPL - original licence link has changed is not relivant.
25581  *
25582  * Fork - LGPL
25583  * <script type="text/javascript">
25584  */
25585  
25586 // Base class for reading structured data from a data source.  This class is intended to be
25587 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25588
25589 /**
25590  * @class Roo.data.DataReader
25591  * @abstract
25592  * Base class for reading structured data from a data source.  This class is intended to be
25593  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25594  */
25595
25596 Roo.data.DataReader = function(meta, recordType){
25597     
25598     this.meta = meta;
25599     
25600     this.recordType = recordType instanceof Array ? 
25601         Roo.data.Record.create(recordType) : recordType;
25602 };
25603
25604 Roo.data.DataReader.prototype = {
25605     
25606     
25607     readerType : 'Data',
25608      /**
25609      * Create an empty record
25610      * @param {Object} data (optional) - overlay some values
25611      * @return {Roo.data.Record} record created.
25612      */
25613     newRow :  function(d) {
25614         var da =  {};
25615         this.recordType.prototype.fields.each(function(c) {
25616             switch( c.type) {
25617                 case 'int' : da[c.name] = 0; break;
25618                 case 'date' : da[c.name] = new Date(); break;
25619                 case 'float' : da[c.name] = 0.0; break;
25620                 case 'boolean' : da[c.name] = false; break;
25621                 default : da[c.name] = ""; break;
25622             }
25623             
25624         });
25625         return new this.recordType(Roo.apply(da, d));
25626     }
25627     
25628     
25629 };/*
25630  * Based on:
25631  * Ext JS Library 1.1.1
25632  * Copyright(c) 2006-2007, Ext JS, LLC.
25633  *
25634  * Originally Released Under LGPL - original licence link has changed is not relivant.
25635  *
25636  * Fork - LGPL
25637  * <script type="text/javascript">
25638  */
25639
25640 /**
25641  * @class Roo.data.DataProxy
25642  * @extends Roo.util.Observable
25643  * @abstract
25644  * This class is an abstract base class for implementations which provide retrieval of
25645  * unformatted data objects.<br>
25646  * <p>
25647  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25648  * (of the appropriate type which knows how to parse the data object) to provide a block of
25649  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25650  * <p>
25651  * Custom implementations must implement the load method as described in
25652  * {@link Roo.data.HttpProxy#load}.
25653  */
25654 Roo.data.DataProxy = function(){
25655     this.addEvents({
25656         /**
25657          * @event beforeload
25658          * Fires before a network request is made to retrieve a data object.
25659          * @param {Object} This DataProxy object.
25660          * @param {Object} params The params parameter to the load function.
25661          */
25662         beforeload : true,
25663         /**
25664          * @event load
25665          * Fires before the load method's callback is called.
25666          * @param {Object} This DataProxy object.
25667          * @param {Object} o The data object.
25668          * @param {Object} arg The callback argument object passed to the load function.
25669          */
25670         load : true,
25671         /**
25672          * @event loadexception
25673          * Fires if an Exception occurs during data retrieval.
25674          * @param {Object} This DataProxy object.
25675          * @param {Object} o The data object.
25676          * @param {Object} arg The callback argument object passed to the load function.
25677          * @param {Object} e The Exception.
25678          */
25679         loadexception : true
25680     });
25681     Roo.data.DataProxy.superclass.constructor.call(this);
25682 };
25683
25684 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25685
25686     /**
25687      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25688      */
25689 /*
25690  * Based on:
25691  * Ext JS Library 1.1.1
25692  * Copyright(c) 2006-2007, Ext JS, LLC.
25693  *
25694  * Originally Released Under LGPL - original licence link has changed is not relivant.
25695  *
25696  * Fork - LGPL
25697  * <script type="text/javascript">
25698  */
25699 /**
25700  * @class Roo.data.MemoryProxy
25701  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25702  * to the Reader when its load method is called.
25703  * @constructor
25704  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25705  */
25706 Roo.data.MemoryProxy = function(data){
25707     if (data.data) {
25708         data = data.data;
25709     }
25710     Roo.data.MemoryProxy.superclass.constructor.call(this);
25711     this.data = data;
25712 };
25713
25714 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25715     
25716     /**
25717      * Load data from the requested source (in this case an in-memory
25718      * data object passed to the constructor), read the data object into
25719      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25720      * process that block using the passed callback.
25721      * @param {Object} params This parameter is not used by the MemoryProxy class.
25722      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25723      * object into a block of Roo.data.Records.
25724      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25725      * The function must be passed <ul>
25726      * <li>The Record block object</li>
25727      * <li>The "arg" argument from the load function</li>
25728      * <li>A boolean success indicator</li>
25729      * </ul>
25730      * @param {Object} scope The scope in which to call the callback
25731      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25732      */
25733     load : function(params, reader, callback, scope, arg){
25734         params = params || {};
25735         var result;
25736         try {
25737             result = reader.readRecords(params.data ? params.data :this.data);
25738         }catch(e){
25739             this.fireEvent("loadexception", this, arg, null, e);
25740             callback.call(scope, null, arg, false);
25741             return;
25742         }
25743         callback.call(scope, result, arg, true);
25744     },
25745     
25746     // private
25747     update : function(params, records){
25748         
25749     }
25750 });/*
25751  * Based on:
25752  * Ext JS Library 1.1.1
25753  * Copyright(c) 2006-2007, Ext JS, LLC.
25754  *
25755  * Originally Released Under LGPL - original licence link has changed is not relivant.
25756  *
25757  * Fork - LGPL
25758  * <script type="text/javascript">
25759  */
25760 /**
25761  * @class Roo.data.HttpProxy
25762  * @extends Roo.data.DataProxy
25763  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25764  * configured to reference a certain URL.<br><br>
25765  * <p>
25766  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25767  * from which the running page was served.<br><br>
25768  * <p>
25769  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25770  * <p>
25771  * Be aware that to enable the browser to parse an XML document, the server must set
25772  * the Content-Type header in the HTTP response to "text/xml".
25773  * @constructor
25774  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25775  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25776  * will be used to make the request.
25777  */
25778 Roo.data.HttpProxy = function(conn){
25779     Roo.data.HttpProxy.superclass.constructor.call(this);
25780     // is conn a conn config or a real conn?
25781     this.conn = conn;
25782     this.useAjax = !conn || !conn.events;
25783   
25784 };
25785
25786 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25787     // thse are take from connection...
25788     
25789     /**
25790      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25791      */
25792     /**
25793      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25794      * extra parameters to each request made by this object. (defaults to undefined)
25795      */
25796     /**
25797      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25798      *  to each request made by this object. (defaults to undefined)
25799      */
25800     /**
25801      * @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)
25802      */
25803     /**
25804      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25805      */
25806      /**
25807      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25808      * @type Boolean
25809      */
25810   
25811
25812     /**
25813      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25814      * @type Boolean
25815      */
25816     /**
25817      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25818      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25819      * a finer-grained basis than the DataProxy events.
25820      */
25821     getConnection : function(){
25822         return this.useAjax ? Roo.Ajax : this.conn;
25823     },
25824
25825     /**
25826      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25827      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25828      * process that block using the passed callback.
25829      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25830      * for the request to the remote server.
25831      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25832      * object into a block of Roo.data.Records.
25833      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25834      * The function must be passed <ul>
25835      * <li>The Record block object</li>
25836      * <li>The "arg" argument from the load function</li>
25837      * <li>A boolean success indicator</li>
25838      * </ul>
25839      * @param {Object} scope The scope in which to call the callback
25840      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25841      */
25842     load : function(params, reader, callback, scope, arg){
25843         if(this.fireEvent("beforeload", this, params) !== false){
25844             var  o = {
25845                 params : params || {},
25846                 request: {
25847                     callback : callback,
25848                     scope : scope,
25849                     arg : arg
25850                 },
25851                 reader: reader,
25852                 callback : this.loadResponse,
25853                 scope: this
25854             };
25855             if(this.useAjax){
25856                 Roo.applyIf(o, this.conn);
25857                 if(this.activeRequest){
25858                     Roo.Ajax.abort(this.activeRequest);
25859                 }
25860                 this.activeRequest = Roo.Ajax.request(o);
25861             }else{
25862                 this.conn.request(o);
25863             }
25864         }else{
25865             callback.call(scope||this, null, arg, false);
25866         }
25867     },
25868
25869     // private
25870     loadResponse : function(o, success, response){
25871         delete this.activeRequest;
25872         if(!success){
25873             this.fireEvent("loadexception", this, o, response);
25874             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25875             return;
25876         }
25877         var result;
25878         try {
25879             result = o.reader.read(response);
25880         }catch(e){
25881             o.success = false;
25882             o.raw = { errorMsg : response.responseText };
25883             this.fireEvent("loadexception", this, o, response, e);
25884             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25885             return;
25886         }
25887         
25888         this.fireEvent("load", this, o, o.request.arg);
25889         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25890     },
25891
25892     // private
25893     update : function(dataSet){
25894
25895     },
25896
25897     // private
25898     updateResponse : function(dataSet){
25899
25900     }
25901 });/*
25902  * Based on:
25903  * Ext JS Library 1.1.1
25904  * Copyright(c) 2006-2007, Ext JS, LLC.
25905  *
25906  * Originally Released Under LGPL - original licence link has changed is not relivant.
25907  *
25908  * Fork - LGPL
25909  * <script type="text/javascript">
25910  */
25911
25912 /**
25913  * @class Roo.data.ScriptTagProxy
25914  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25915  * other than the originating domain of the running page.<br><br>
25916  * <p>
25917  * <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
25918  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25919  * <p>
25920  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25921  * source code that is used as the source inside a &lt;script> tag.<br><br>
25922  * <p>
25923  * In order for the browser to process the returned data, the server must wrap the data object
25924  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25925  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25926  * depending on whether the callback name was passed:
25927  * <p>
25928  * <pre><code>
25929 boolean scriptTag = false;
25930 String cb = request.getParameter("callback");
25931 if (cb != null) {
25932     scriptTag = true;
25933     response.setContentType("text/javascript");
25934 } else {
25935     response.setContentType("application/x-json");
25936 }
25937 Writer out = response.getWriter();
25938 if (scriptTag) {
25939     out.write(cb + "(");
25940 }
25941 out.print(dataBlock.toJsonString());
25942 if (scriptTag) {
25943     out.write(");");
25944 }
25945 </pre></code>
25946  *
25947  * @constructor
25948  * @param {Object} config A configuration object.
25949  */
25950 Roo.data.ScriptTagProxy = function(config){
25951     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25952     Roo.apply(this, config);
25953     this.head = document.getElementsByTagName("head")[0];
25954 };
25955
25956 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25957
25958 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25959     /**
25960      * @cfg {String} url The URL from which to request the data object.
25961      */
25962     /**
25963      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25964      */
25965     timeout : 30000,
25966     /**
25967      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25968      * the server the name of the callback function set up by the load call to process the returned data object.
25969      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25970      * javascript output which calls this named function passing the data object as its only parameter.
25971      */
25972     callbackParam : "callback",
25973     /**
25974      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25975      * name to the request.
25976      */
25977     nocache : true,
25978
25979     /**
25980      * Load data from the configured URL, read the data object into
25981      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25982      * process that block using the passed callback.
25983      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25984      * for the request to the remote server.
25985      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25986      * object into a block of Roo.data.Records.
25987      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25988      * The function must be passed <ul>
25989      * <li>The Record block object</li>
25990      * <li>The "arg" argument from the load function</li>
25991      * <li>A boolean success indicator</li>
25992      * </ul>
25993      * @param {Object} scope The scope in which to call the callback
25994      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25995      */
25996     load : function(params, reader, callback, scope, arg){
25997         if(this.fireEvent("beforeload", this, params) !== false){
25998
25999             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
26000
26001             var url = this.url;
26002             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
26003             if(this.nocache){
26004                 url += "&_dc=" + (new Date().getTime());
26005             }
26006             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
26007             var trans = {
26008                 id : transId,
26009                 cb : "stcCallback"+transId,
26010                 scriptId : "stcScript"+transId,
26011                 params : params,
26012                 arg : arg,
26013                 url : url,
26014                 callback : callback,
26015                 scope : scope,
26016                 reader : reader
26017             };
26018             var conn = this;
26019
26020             window[trans.cb] = function(o){
26021                 conn.handleResponse(o, trans);
26022             };
26023
26024             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26025
26026             if(this.autoAbort !== false){
26027                 this.abort();
26028             }
26029
26030             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26031
26032             var script = document.createElement("script");
26033             script.setAttribute("src", url);
26034             script.setAttribute("type", "text/javascript");
26035             script.setAttribute("id", trans.scriptId);
26036             this.head.appendChild(script);
26037
26038             this.trans = trans;
26039         }else{
26040             callback.call(scope||this, null, arg, false);
26041         }
26042     },
26043
26044     // private
26045     isLoading : function(){
26046         return this.trans ? true : false;
26047     },
26048
26049     /**
26050      * Abort the current server request.
26051      */
26052     abort : function(){
26053         if(this.isLoading()){
26054             this.destroyTrans(this.trans);
26055         }
26056     },
26057
26058     // private
26059     destroyTrans : function(trans, isLoaded){
26060         this.head.removeChild(document.getElementById(trans.scriptId));
26061         clearTimeout(trans.timeoutId);
26062         if(isLoaded){
26063             window[trans.cb] = undefined;
26064             try{
26065                 delete window[trans.cb];
26066             }catch(e){}
26067         }else{
26068             // if hasn't been loaded, wait for load to remove it to prevent script error
26069             window[trans.cb] = function(){
26070                 window[trans.cb] = undefined;
26071                 try{
26072                     delete window[trans.cb];
26073                 }catch(e){}
26074             };
26075         }
26076     },
26077
26078     // private
26079     handleResponse : function(o, trans){
26080         this.trans = false;
26081         this.destroyTrans(trans, true);
26082         var result;
26083         try {
26084             result = trans.reader.readRecords(o);
26085         }catch(e){
26086             this.fireEvent("loadexception", this, o, trans.arg, e);
26087             trans.callback.call(trans.scope||window, null, trans.arg, false);
26088             return;
26089         }
26090         this.fireEvent("load", this, o, trans.arg);
26091         trans.callback.call(trans.scope||window, result, trans.arg, true);
26092     },
26093
26094     // private
26095     handleFailure : function(trans){
26096         this.trans = false;
26097         this.destroyTrans(trans, false);
26098         this.fireEvent("loadexception", this, null, trans.arg);
26099         trans.callback.call(trans.scope||window, null, trans.arg, false);
26100     }
26101 });/*
26102  * Based on:
26103  * Ext JS Library 1.1.1
26104  * Copyright(c) 2006-2007, Ext JS, LLC.
26105  *
26106  * Originally Released Under LGPL - original licence link has changed is not relivant.
26107  *
26108  * Fork - LGPL
26109  * <script type="text/javascript">
26110  */
26111
26112 /**
26113  * @class Roo.data.JsonReader
26114  * @extends Roo.data.DataReader
26115  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26116  * based on mappings in a provided Roo.data.Record constructor.
26117  * 
26118  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26119  * in the reply previously. 
26120  * 
26121  * <p>
26122  * Example code:
26123  * <pre><code>
26124 var RecordDef = Roo.data.Record.create([
26125     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26126     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26127 ]);
26128 var myReader = new Roo.data.JsonReader({
26129     totalProperty: "results",    // The property which contains the total dataset size (optional)
26130     root: "rows",                // The property which contains an Array of row objects
26131     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26132 }, RecordDef);
26133 </code></pre>
26134  * <p>
26135  * This would consume a JSON file like this:
26136  * <pre><code>
26137 { 'results': 2, 'rows': [
26138     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26139     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26140 }
26141 </code></pre>
26142  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26143  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26144  * paged from the remote server.
26145  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26146  * @cfg {String} root name of the property which contains the Array of row objects.
26147  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26148  * @cfg {Array} fields Array of field definition objects
26149  * @constructor
26150  * Create a new JsonReader
26151  * @param {Object} meta Metadata configuration options
26152  * @param {Object} recordType Either an Array of field definition objects,
26153  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26154  */
26155 Roo.data.JsonReader = function(meta, recordType){
26156     
26157     meta = meta || {};
26158     // set some defaults:
26159     Roo.applyIf(meta, {
26160         totalProperty: 'total',
26161         successProperty : 'success',
26162         root : 'data',
26163         id : 'id'
26164     });
26165     
26166     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26167 };
26168 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26169     
26170     readerType : 'Json',
26171     
26172     /**
26173      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26174      * Used by Store query builder to append _requestMeta to params.
26175      * 
26176      */
26177     metaFromRemote : false,
26178     /**
26179      * This method is only used by a DataProxy which has retrieved data from a remote server.
26180      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26181      * @return {Object} data A data block which is used by an Roo.data.Store object as
26182      * a cache of Roo.data.Records.
26183      */
26184     read : function(response){
26185         var json = response.responseText;
26186        
26187         var o = /* eval:var:o */ eval("("+json+")");
26188         if(!o) {
26189             throw {message: "JsonReader.read: Json object not found"};
26190         }
26191         
26192         if(o.metaData){
26193             
26194             delete this.ef;
26195             this.metaFromRemote = true;
26196             this.meta = o.metaData;
26197             this.recordType = Roo.data.Record.create(o.metaData.fields);
26198             this.onMetaChange(this.meta, this.recordType, o);
26199         }
26200         return this.readRecords(o);
26201     },
26202
26203     // private function a store will implement
26204     onMetaChange : function(meta, recordType, o){
26205
26206     },
26207
26208     /**
26209          * @ignore
26210          */
26211     simpleAccess: function(obj, subsc) {
26212         return obj[subsc];
26213     },
26214
26215         /**
26216          * @ignore
26217          */
26218     getJsonAccessor: function(){
26219         var re = /[\[\.]/;
26220         return function(expr) {
26221             try {
26222                 return(re.test(expr))
26223                     ? new Function("obj", "return obj." + expr)
26224                     : function(obj){
26225                         return obj[expr];
26226                     };
26227             } catch(e){}
26228             return Roo.emptyFn;
26229         };
26230     }(),
26231
26232     /**
26233      * Create a data block containing Roo.data.Records from an XML document.
26234      * @param {Object} o An object which contains an Array of row objects in the property specified
26235      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26236      * which contains the total size of the dataset.
26237      * @return {Object} data A data block which is used by an Roo.data.Store object as
26238      * a cache of Roo.data.Records.
26239      */
26240     readRecords : function(o){
26241         /**
26242          * After any data loads, the raw JSON data is available for further custom processing.
26243          * @type Object
26244          */
26245         this.o = o;
26246         var s = this.meta, Record = this.recordType,
26247             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26248
26249 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26250         if (!this.ef) {
26251             if(s.totalProperty) {
26252                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26253                 }
26254                 if(s.successProperty) {
26255                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26256                 }
26257                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26258                 if (s.id) {
26259                         var g = this.getJsonAccessor(s.id);
26260                         this.getId = function(rec) {
26261                                 var r = g(rec);  
26262                                 return (r === undefined || r === "") ? null : r;
26263                         };
26264                 } else {
26265                         this.getId = function(){return null;};
26266                 }
26267             this.ef = [];
26268             for(var jj = 0; jj < fl; jj++){
26269                 f = fi[jj];
26270                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26271                 this.ef[jj] = this.getJsonAccessor(map);
26272             }
26273         }
26274
26275         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26276         if(s.totalProperty){
26277             var vt = parseInt(this.getTotal(o), 10);
26278             if(!isNaN(vt)){
26279                 totalRecords = vt;
26280             }
26281         }
26282         if(s.successProperty){
26283             var vs = this.getSuccess(o);
26284             if(vs === false || vs === 'false'){
26285                 success = false;
26286             }
26287         }
26288         var records = [];
26289         for(var i = 0; i < c; i++){
26290             var n = root[i];
26291             var values = {};
26292             var id = this.getId(n);
26293             for(var j = 0; j < fl; j++){
26294                 f = fi[j];
26295                                 var v = this.ef[j](n);
26296                                 if (!f.convert) {
26297                                         Roo.log('missing convert for ' + f.name);
26298                                         Roo.log(f);
26299                                         continue;
26300                                 }
26301                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26302             }
26303                         if (!Record) {
26304                                 return {
26305                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26306                                         success : false,
26307                                         records : [],
26308                                         totalRecords : 0
26309                                 };
26310                         }
26311             var record = new Record(values, id);
26312             record.json = n;
26313             records[i] = record;
26314         }
26315         return {
26316             raw : o,
26317             success : success,
26318             records : records,
26319             totalRecords : totalRecords
26320         };
26321     },
26322     // used when loading children.. @see loadDataFromChildren
26323     toLoadData: function(rec)
26324     {
26325         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26326         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26327         return { data : data, total : data.length };
26328         
26329     }
26330 });/*
26331  * Based on:
26332  * Ext JS Library 1.1.1
26333  * Copyright(c) 2006-2007, Ext JS, LLC.
26334  *
26335  * Originally Released Under LGPL - original licence link has changed is not relivant.
26336  *
26337  * Fork - LGPL
26338  * <script type="text/javascript">
26339  */
26340
26341 /**
26342  * @class Roo.data.XmlReader
26343  * @extends Roo.data.DataReader
26344  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26345  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26346  * <p>
26347  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26348  * header in the HTTP response must be set to "text/xml".</em>
26349  * <p>
26350  * Example code:
26351  * <pre><code>
26352 var RecordDef = Roo.data.Record.create([
26353    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26354    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26355 ]);
26356 var myReader = new Roo.data.XmlReader({
26357    totalRecords: "results", // The element which contains the total dataset size (optional)
26358    record: "row",           // The repeated element which contains row information
26359    id: "id"                 // The element within the row that provides an ID for the record (optional)
26360 }, RecordDef);
26361 </code></pre>
26362  * <p>
26363  * This would consume an XML file like this:
26364  * <pre><code>
26365 &lt;?xml?>
26366 &lt;dataset>
26367  &lt;results>2&lt;/results>
26368  &lt;row>
26369    &lt;id>1&lt;/id>
26370    &lt;name>Bill&lt;/name>
26371    &lt;occupation>Gardener&lt;/occupation>
26372  &lt;/row>
26373  &lt;row>
26374    &lt;id>2&lt;/id>
26375    &lt;name>Ben&lt;/name>
26376    &lt;occupation>Horticulturalist&lt;/occupation>
26377  &lt;/row>
26378 &lt;/dataset>
26379 </code></pre>
26380  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26381  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26382  * paged from the remote server.
26383  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26384  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26385  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26386  * a record identifier value.
26387  * @constructor
26388  * Create a new XmlReader
26389  * @param {Object} meta Metadata configuration options
26390  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26391  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26392  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26393  */
26394 Roo.data.XmlReader = function(meta, recordType){
26395     meta = meta || {};
26396     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26397 };
26398 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26399     
26400     readerType : 'Xml',
26401     
26402     /**
26403      * This method is only used by a DataProxy which has retrieved data from a remote server.
26404          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26405          * to contain a method called 'responseXML' that returns an XML document object.
26406      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26407      * a cache of Roo.data.Records.
26408      */
26409     read : function(response){
26410         var doc = response.responseXML;
26411         if(!doc) {
26412             throw {message: "XmlReader.read: XML Document not available"};
26413         }
26414         return this.readRecords(doc);
26415     },
26416
26417     /**
26418      * Create a data block containing Roo.data.Records from an XML document.
26419          * @param {Object} doc A parsed XML document.
26420      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26421      * a cache of Roo.data.Records.
26422      */
26423     readRecords : function(doc){
26424         /**
26425          * After any data loads/reads, the raw XML Document is available for further custom processing.
26426          * @type XMLDocument
26427          */
26428         this.xmlData = doc;
26429         var root = doc.documentElement || doc;
26430         var q = Roo.DomQuery;
26431         var recordType = this.recordType, fields = recordType.prototype.fields;
26432         var sid = this.meta.id;
26433         var totalRecords = 0, success = true;
26434         if(this.meta.totalRecords){
26435             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26436         }
26437         
26438         if(this.meta.success){
26439             var sv = q.selectValue(this.meta.success, root, true);
26440             success = sv !== false && sv !== 'false';
26441         }
26442         var records = [];
26443         var ns = q.select(this.meta.record, root);
26444         for(var i = 0, len = ns.length; i < len; i++) {
26445                 var n = ns[i];
26446                 var values = {};
26447                 var id = sid ? q.selectValue(sid, n) : undefined;
26448                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26449                     var f = fields.items[j];
26450                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26451                     v = f.convert(v);
26452                     values[f.name] = v;
26453                 }
26454                 var record = new recordType(values, id);
26455                 record.node = n;
26456                 records[records.length] = record;
26457             }
26458
26459             return {
26460                 success : success,
26461                 records : records,
26462                 totalRecords : totalRecords || records.length
26463             };
26464     }
26465 });/*
26466  * Based on:
26467  * Ext JS Library 1.1.1
26468  * Copyright(c) 2006-2007, Ext JS, LLC.
26469  *
26470  * Originally Released Under LGPL - original licence link has changed is not relivant.
26471  *
26472  * Fork - LGPL
26473  * <script type="text/javascript">
26474  */
26475
26476 /**
26477  * @class Roo.data.ArrayReader
26478  * @extends Roo.data.DataReader
26479  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26480  * Each element of that Array represents a row of data fields. The
26481  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26482  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26483  * <p>
26484  * Example code:.
26485  * <pre><code>
26486 var RecordDef = Roo.data.Record.create([
26487     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26488     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26489 ]);
26490 var myReader = new Roo.data.ArrayReader({
26491     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26492 }, RecordDef);
26493 </code></pre>
26494  * <p>
26495  * This would consume an Array like this:
26496  * <pre><code>
26497 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26498   </code></pre>
26499  
26500  * @constructor
26501  * Create a new JsonReader
26502  * @param {Object} meta Metadata configuration options.
26503  * @param {Object|Array} recordType Either an Array of field definition objects
26504  * 
26505  * @cfg {Array} fields Array of field definition objects
26506  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26507  * as specified to {@link Roo.data.Record#create},
26508  * or an {@link Roo.data.Record} object
26509  *
26510  * 
26511  * created using {@link Roo.data.Record#create}.
26512  */
26513 Roo.data.ArrayReader = function(meta, recordType)
26514 {    
26515     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26516 };
26517
26518 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26519     
26520       /**
26521      * Create a data block containing Roo.data.Records from an XML document.
26522      * @param {Object} o An Array of row objects which represents the dataset.
26523      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26524      * a cache of Roo.data.Records.
26525      */
26526     readRecords : function(o)
26527     {
26528         var sid = this.meta ? this.meta.id : null;
26529         var recordType = this.recordType, fields = recordType.prototype.fields;
26530         var records = [];
26531         var root = o;
26532         for(var i = 0; i < root.length; i++){
26533             var n = root[i];
26534             var values = {};
26535             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26536             for(var j = 0, jlen = fields.length; j < jlen; j++){
26537                 var f = fields.items[j];
26538                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26539                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26540                 v = f.convert(v);
26541                 values[f.name] = v;
26542             }
26543             var record = new recordType(values, id);
26544             record.json = n;
26545             records[records.length] = record;
26546         }
26547         return {
26548             records : records,
26549             totalRecords : records.length
26550         };
26551     },
26552     // used when loading children.. @see loadDataFromChildren
26553     toLoadData: function(rec)
26554     {
26555         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26556         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26557         
26558     }
26559     
26560     
26561 });/*
26562  * Based on:
26563  * Ext JS Library 1.1.1
26564  * Copyright(c) 2006-2007, Ext JS, LLC.
26565  *
26566  * Originally Released Under LGPL - original licence link has changed is not relivant.
26567  *
26568  * Fork - LGPL
26569  * <script type="text/javascript">
26570  */
26571
26572
26573 /**
26574  * @class Roo.data.Tree
26575  * @extends Roo.util.Observable
26576  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26577  * in the tree have most standard DOM functionality.
26578  * @constructor
26579  * @param {Node} root (optional) The root node
26580  */
26581 Roo.data.Tree = function(root){
26582    this.nodeHash = {};
26583    /**
26584     * The root node for this tree
26585     * @type Node
26586     */
26587    this.root = null;
26588    if(root){
26589        this.setRootNode(root);
26590    }
26591    this.addEvents({
26592        /**
26593         * @event append
26594         * Fires when a new child node is appended to a node in this tree.
26595         * @param {Tree} tree The owner tree
26596         * @param {Node} parent The parent node
26597         * @param {Node} node The newly appended node
26598         * @param {Number} index The index of the newly appended node
26599         */
26600        "append" : true,
26601        /**
26602         * @event remove
26603         * Fires when a child node is removed from a node in this tree.
26604         * @param {Tree} tree The owner tree
26605         * @param {Node} parent The parent node
26606         * @param {Node} node The child node removed
26607         */
26608        "remove" : true,
26609        /**
26610         * @event move
26611         * Fires when a node is moved to a new location in the tree
26612         * @param {Tree} tree The owner tree
26613         * @param {Node} node The node moved
26614         * @param {Node} oldParent The old parent of this node
26615         * @param {Node} newParent The new parent of this node
26616         * @param {Number} index The index it was moved to
26617         */
26618        "move" : true,
26619        /**
26620         * @event insert
26621         * Fires when a new child node is inserted in a node in this tree.
26622         * @param {Tree} tree The owner tree
26623         * @param {Node} parent The parent node
26624         * @param {Node} node The child node inserted
26625         * @param {Node} refNode The child node the node was inserted before
26626         */
26627        "insert" : true,
26628        /**
26629         * @event beforeappend
26630         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26631         * @param {Tree} tree The owner tree
26632         * @param {Node} parent The parent node
26633         * @param {Node} node The child node to be appended
26634         */
26635        "beforeappend" : true,
26636        /**
26637         * @event beforeremove
26638         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26639         * @param {Tree} tree The owner tree
26640         * @param {Node} parent The parent node
26641         * @param {Node} node The child node to be removed
26642         */
26643        "beforeremove" : true,
26644        /**
26645         * @event beforemove
26646         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26647         * @param {Tree} tree The owner tree
26648         * @param {Node} node The node being moved
26649         * @param {Node} oldParent The parent of the node
26650         * @param {Node} newParent The new parent the node is moving to
26651         * @param {Number} index The index it is being moved to
26652         */
26653        "beforemove" : true,
26654        /**
26655         * @event beforeinsert
26656         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26657         * @param {Tree} tree The owner tree
26658         * @param {Node} parent The parent node
26659         * @param {Node} node The child node to be inserted
26660         * @param {Node} refNode The child node the node is being inserted before
26661         */
26662        "beforeinsert" : true
26663    });
26664
26665     Roo.data.Tree.superclass.constructor.call(this);
26666 };
26667
26668 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26669     pathSeparator: "/",
26670
26671     proxyNodeEvent : function(){
26672         return this.fireEvent.apply(this, arguments);
26673     },
26674
26675     /**
26676      * Returns the root node for this tree.
26677      * @return {Node}
26678      */
26679     getRootNode : function(){
26680         return this.root;
26681     },
26682
26683     /**
26684      * Sets the root node for this tree.
26685      * @param {Node} node
26686      * @return {Node}
26687      */
26688     setRootNode : function(node){
26689         this.root = node;
26690         node.ownerTree = this;
26691         node.isRoot = true;
26692         this.registerNode(node);
26693         return node;
26694     },
26695
26696     /**
26697      * Gets a node in this tree by its id.
26698      * @param {String} id
26699      * @return {Node}
26700      */
26701     getNodeById : function(id){
26702         return this.nodeHash[id];
26703     },
26704
26705     registerNode : function(node){
26706         this.nodeHash[node.id] = node;
26707     },
26708
26709     unregisterNode : function(node){
26710         delete this.nodeHash[node.id];
26711     },
26712
26713     toString : function(){
26714         return "[Tree"+(this.id?" "+this.id:"")+"]";
26715     }
26716 });
26717
26718 /**
26719  * @class Roo.data.Node
26720  * @extends Roo.util.Observable
26721  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26722  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26723  * @constructor
26724  * @param {Object} attributes The attributes/config for the node
26725  */
26726 Roo.data.Node = function(attributes){
26727     /**
26728      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26729      * @type {Object}
26730      */
26731     this.attributes = attributes || {};
26732     this.leaf = this.attributes.leaf;
26733     /**
26734      * The node id. @type String
26735      */
26736     this.id = this.attributes.id;
26737     if(!this.id){
26738         this.id = Roo.id(null, "ynode-");
26739         this.attributes.id = this.id;
26740     }
26741      
26742     
26743     /**
26744      * All child nodes of this node. @type Array
26745      */
26746     this.childNodes = [];
26747     if(!this.childNodes.indexOf){ // indexOf is a must
26748         this.childNodes.indexOf = function(o){
26749             for(var i = 0, len = this.length; i < len; i++){
26750                 if(this[i] == o) {
26751                     return i;
26752                 }
26753             }
26754             return -1;
26755         };
26756     }
26757     /**
26758      * The parent node for this node. @type Node
26759      */
26760     this.parentNode = null;
26761     /**
26762      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26763      */
26764     this.firstChild = null;
26765     /**
26766      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26767      */
26768     this.lastChild = null;
26769     /**
26770      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26771      */
26772     this.previousSibling = null;
26773     /**
26774      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26775      */
26776     this.nextSibling = null;
26777
26778     this.addEvents({
26779        /**
26780         * @event append
26781         * Fires when a new child node is appended
26782         * @param {Tree} tree The owner tree
26783         * @param {Node} this This node
26784         * @param {Node} node The newly appended node
26785         * @param {Number} index The index of the newly appended node
26786         */
26787        "append" : true,
26788        /**
26789         * @event remove
26790         * Fires when a child node is removed
26791         * @param {Tree} tree The owner tree
26792         * @param {Node} this This node
26793         * @param {Node} node The removed node
26794         */
26795        "remove" : true,
26796        /**
26797         * @event move
26798         * Fires when this node is moved to a new location in the tree
26799         * @param {Tree} tree The owner tree
26800         * @param {Node} this This node
26801         * @param {Node} oldParent The old parent of this node
26802         * @param {Node} newParent The new parent of this node
26803         * @param {Number} index The index it was moved to
26804         */
26805        "move" : true,
26806        /**
26807         * @event insert
26808         * Fires when a new child node is inserted.
26809         * @param {Tree} tree The owner tree
26810         * @param {Node} this This node
26811         * @param {Node} node The child node inserted
26812         * @param {Node} refNode The child node the node was inserted before
26813         */
26814        "insert" : true,
26815        /**
26816         * @event beforeappend
26817         * Fires before a new child is appended, return false to cancel the append.
26818         * @param {Tree} tree The owner tree
26819         * @param {Node} this This node
26820         * @param {Node} node The child node to be appended
26821         */
26822        "beforeappend" : true,
26823        /**
26824         * @event beforeremove
26825         * Fires before a child is removed, return false to cancel the remove.
26826         * @param {Tree} tree The owner tree
26827         * @param {Node} this This node
26828         * @param {Node} node The child node to be removed
26829         */
26830        "beforeremove" : true,
26831        /**
26832         * @event beforemove
26833         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26834         * @param {Tree} tree The owner tree
26835         * @param {Node} this This node
26836         * @param {Node} oldParent The parent of this node
26837         * @param {Node} newParent The new parent this node is moving to
26838         * @param {Number} index The index it is being moved to
26839         */
26840        "beforemove" : true,
26841        /**
26842         * @event beforeinsert
26843         * Fires before a new child is inserted, return false to cancel the insert.
26844         * @param {Tree} tree The owner tree
26845         * @param {Node} this This node
26846         * @param {Node} node The child node to be inserted
26847         * @param {Node} refNode The child node the node is being inserted before
26848         */
26849        "beforeinsert" : true
26850    });
26851     this.listeners = this.attributes.listeners;
26852     Roo.data.Node.superclass.constructor.call(this);
26853 };
26854
26855 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26856     fireEvent : function(evtName){
26857         // first do standard event for this node
26858         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26859             return false;
26860         }
26861         // then bubble it up to the tree if the event wasn't cancelled
26862         var ot = this.getOwnerTree();
26863         if(ot){
26864             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26865                 return false;
26866             }
26867         }
26868         return true;
26869     },
26870
26871     /**
26872      * Returns true if this node is a leaf
26873      * @return {Boolean}
26874      */
26875     isLeaf : function(){
26876         return this.leaf === true;
26877     },
26878
26879     // private
26880     setFirstChild : function(node){
26881         this.firstChild = node;
26882     },
26883
26884     //private
26885     setLastChild : function(node){
26886         this.lastChild = node;
26887     },
26888
26889
26890     /**
26891      * Returns true if this node is the last child of its parent
26892      * @return {Boolean}
26893      */
26894     isLast : function(){
26895        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26896     },
26897
26898     /**
26899      * Returns true if this node is the first child of its parent
26900      * @return {Boolean}
26901      */
26902     isFirst : function(){
26903        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26904     },
26905
26906     hasChildNodes : function(){
26907         return !this.isLeaf() && this.childNodes.length > 0;
26908     },
26909
26910     /**
26911      * Insert node(s) as the last child node of this node.
26912      * @param {Node/Array} node The node or Array of nodes to append
26913      * @return {Node} The appended node if single append, or null if an array was passed
26914      */
26915     appendChild : function(node){
26916         var multi = false;
26917         if(node instanceof Array){
26918             multi = node;
26919         }else if(arguments.length > 1){
26920             multi = arguments;
26921         }
26922         
26923         // if passed an array or multiple args do them one by one
26924         if(multi){
26925             for(var i = 0, len = multi.length; i < len; i++) {
26926                 this.appendChild(multi[i]);
26927             }
26928         }else{
26929             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26930                 return false;
26931             }
26932             var index = this.childNodes.length;
26933             var oldParent = node.parentNode;
26934             // it's a move, make sure we move it cleanly
26935             if(oldParent){
26936                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26937                     return false;
26938                 }
26939                 oldParent.removeChild(node);
26940             }
26941             
26942             index = this.childNodes.length;
26943             if(index == 0){
26944                 this.setFirstChild(node);
26945             }
26946             this.childNodes.push(node);
26947             node.parentNode = this;
26948             var ps = this.childNodes[index-1];
26949             if(ps){
26950                 node.previousSibling = ps;
26951                 ps.nextSibling = node;
26952             }else{
26953                 node.previousSibling = null;
26954             }
26955             node.nextSibling = null;
26956             this.setLastChild(node);
26957             node.setOwnerTree(this.getOwnerTree());
26958             this.fireEvent("append", this.ownerTree, this, node, index);
26959             if(this.ownerTree) {
26960                 this.ownerTree.fireEvent("appendnode", this, node, index);
26961             }
26962             if(oldParent){
26963                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26964             }
26965             return node;
26966         }
26967     },
26968
26969     /**
26970      * Removes a child node from this node.
26971      * @param {Node} node The node to remove
26972      * @return {Node} The removed node
26973      */
26974     removeChild : function(node){
26975         var index = this.childNodes.indexOf(node);
26976         if(index == -1){
26977             return false;
26978         }
26979         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26980             return false;
26981         }
26982
26983         // remove it from childNodes collection
26984         this.childNodes.splice(index, 1);
26985
26986         // update siblings
26987         if(node.previousSibling){
26988             node.previousSibling.nextSibling = node.nextSibling;
26989         }
26990         if(node.nextSibling){
26991             node.nextSibling.previousSibling = node.previousSibling;
26992         }
26993
26994         // update child refs
26995         if(this.firstChild == node){
26996             this.setFirstChild(node.nextSibling);
26997         }
26998         if(this.lastChild == node){
26999             this.setLastChild(node.previousSibling);
27000         }
27001
27002         node.setOwnerTree(null);
27003         // clear any references from the node
27004         node.parentNode = null;
27005         node.previousSibling = null;
27006         node.nextSibling = null;
27007         this.fireEvent("remove", this.ownerTree, this, node);
27008         return node;
27009     },
27010
27011     /**
27012      * Inserts the first node before the second node in this nodes childNodes collection.
27013      * @param {Node} node The node to insert
27014      * @param {Node} refNode The node to insert before (if null the node is appended)
27015      * @return {Node} The inserted node
27016      */
27017     insertBefore : function(node, refNode){
27018         if(!refNode){ // like standard Dom, refNode can be null for append
27019             return this.appendChild(node);
27020         }
27021         // nothing to do
27022         if(node == refNode){
27023             return false;
27024         }
27025
27026         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27027             return false;
27028         }
27029         var index = this.childNodes.indexOf(refNode);
27030         var oldParent = node.parentNode;
27031         var refIndex = index;
27032
27033         // when moving internally, indexes will change after remove
27034         if(oldParent == this && this.childNodes.indexOf(node) < index){
27035             refIndex--;
27036         }
27037
27038         // it's a move, make sure we move it cleanly
27039         if(oldParent){
27040             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27041                 return false;
27042             }
27043             oldParent.removeChild(node);
27044         }
27045         if(refIndex == 0){
27046             this.setFirstChild(node);
27047         }
27048         this.childNodes.splice(refIndex, 0, node);
27049         node.parentNode = this;
27050         var ps = this.childNodes[refIndex-1];
27051         if(ps){
27052             node.previousSibling = ps;
27053             ps.nextSibling = node;
27054         }else{
27055             node.previousSibling = null;
27056         }
27057         node.nextSibling = refNode;
27058         refNode.previousSibling = node;
27059         node.setOwnerTree(this.getOwnerTree());
27060         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27061         if(oldParent){
27062             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27063         }
27064         return node;
27065     },
27066
27067     /**
27068      * Returns the child node at the specified index.
27069      * @param {Number} index
27070      * @return {Node}
27071      */
27072     item : function(index){
27073         return this.childNodes[index];
27074     },
27075
27076     /**
27077      * Replaces one child node in this node with another.
27078      * @param {Node} newChild The replacement node
27079      * @param {Node} oldChild The node to replace
27080      * @return {Node} The replaced node
27081      */
27082     replaceChild : function(newChild, oldChild){
27083         this.insertBefore(newChild, oldChild);
27084         this.removeChild(oldChild);
27085         return oldChild;
27086     },
27087
27088     /**
27089      * Returns the index of a child node
27090      * @param {Node} node
27091      * @return {Number} The index of the node or -1 if it was not found
27092      */
27093     indexOf : function(child){
27094         return this.childNodes.indexOf(child);
27095     },
27096
27097     /**
27098      * Returns the tree this node is in.
27099      * @return {Tree}
27100      */
27101     getOwnerTree : function(){
27102         // if it doesn't have one, look for one
27103         if(!this.ownerTree){
27104             var p = this;
27105             while(p){
27106                 if(p.ownerTree){
27107                     this.ownerTree = p.ownerTree;
27108                     break;
27109                 }
27110                 p = p.parentNode;
27111             }
27112         }
27113         return this.ownerTree;
27114     },
27115
27116     /**
27117      * Returns depth of this node (the root node has a depth of 0)
27118      * @return {Number}
27119      */
27120     getDepth : function(){
27121         var depth = 0;
27122         var p = this;
27123         while(p.parentNode){
27124             ++depth;
27125             p = p.parentNode;
27126         }
27127         return depth;
27128     },
27129
27130     // private
27131     setOwnerTree : function(tree){
27132         // if it's move, we need to update everyone
27133         if(tree != this.ownerTree){
27134             if(this.ownerTree){
27135                 this.ownerTree.unregisterNode(this);
27136             }
27137             this.ownerTree = tree;
27138             var cs = this.childNodes;
27139             for(var i = 0, len = cs.length; i < len; i++) {
27140                 cs[i].setOwnerTree(tree);
27141             }
27142             if(tree){
27143                 tree.registerNode(this);
27144             }
27145         }
27146     },
27147
27148     /**
27149      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27150      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27151      * @return {String} The path
27152      */
27153     getPath : function(attr){
27154         attr = attr || "id";
27155         var p = this.parentNode;
27156         var b = [this.attributes[attr]];
27157         while(p){
27158             b.unshift(p.attributes[attr]);
27159             p = p.parentNode;
27160         }
27161         var sep = this.getOwnerTree().pathSeparator;
27162         return sep + b.join(sep);
27163     },
27164
27165     /**
27166      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27167      * function call will be the scope provided or the current node. The arguments to the function
27168      * will be the args provided or the current node. If the function returns false at any point,
27169      * the bubble is stopped.
27170      * @param {Function} fn The function to call
27171      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27172      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27173      */
27174     bubble : function(fn, scope, args){
27175         var p = this;
27176         while(p){
27177             if(fn.call(scope || p, args || p) === false){
27178                 break;
27179             }
27180             p = p.parentNode;
27181         }
27182     },
27183
27184     /**
27185      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27186      * function call will be the scope provided or the current node. The arguments to the function
27187      * will be the args provided or the current node. If the function returns false at any point,
27188      * the cascade is stopped on that branch.
27189      * @param {Function} fn The function to call
27190      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27191      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27192      */
27193     cascade : function(fn, scope, args){
27194         if(fn.call(scope || this, args || this) !== false){
27195             var cs = this.childNodes;
27196             for(var i = 0, len = cs.length; i < len; i++) {
27197                 cs[i].cascade(fn, scope, args);
27198             }
27199         }
27200     },
27201
27202     /**
27203      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27204      * function call will be the scope provided or the current node. The arguments to the function
27205      * will be the args provided or the current node. If the function returns false at any point,
27206      * the iteration stops.
27207      * @param {Function} fn The function to call
27208      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27209      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27210      */
27211     eachChild : function(fn, scope, args){
27212         var cs = this.childNodes;
27213         for(var i = 0, len = cs.length; i < len; i++) {
27214                 if(fn.call(scope || this, args || cs[i]) === false){
27215                     break;
27216                 }
27217         }
27218     },
27219
27220     /**
27221      * Finds the first child that has the attribute with the specified value.
27222      * @param {String} attribute The attribute name
27223      * @param {Mixed} value The value to search for
27224      * @return {Node} The found child or null if none was found
27225      */
27226     findChild : function(attribute, value){
27227         var cs = this.childNodes;
27228         for(var i = 0, len = cs.length; i < len; i++) {
27229                 if(cs[i].attributes[attribute] == value){
27230                     return cs[i];
27231                 }
27232         }
27233         return null;
27234     },
27235
27236     /**
27237      * Finds the first child by a custom function. The child matches if the function passed
27238      * returns true.
27239      * @param {Function} fn
27240      * @param {Object} scope (optional)
27241      * @return {Node} The found child or null if none was found
27242      */
27243     findChildBy : function(fn, scope){
27244         var cs = this.childNodes;
27245         for(var i = 0, len = cs.length; i < len; i++) {
27246                 if(fn.call(scope||cs[i], cs[i]) === true){
27247                     return cs[i];
27248                 }
27249         }
27250         return null;
27251     },
27252
27253     /**
27254      * Sorts this nodes children using the supplied sort function
27255      * @param {Function} fn
27256      * @param {Object} scope (optional)
27257      */
27258     sort : function(fn, scope){
27259         var cs = this.childNodes;
27260         var len = cs.length;
27261         if(len > 0){
27262             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27263             cs.sort(sortFn);
27264             for(var i = 0; i < len; i++){
27265                 var n = cs[i];
27266                 n.previousSibling = cs[i-1];
27267                 n.nextSibling = cs[i+1];
27268                 if(i == 0){
27269                     this.setFirstChild(n);
27270                 }
27271                 if(i == len-1){
27272                     this.setLastChild(n);
27273                 }
27274             }
27275         }
27276     },
27277
27278     /**
27279      * Returns true if this node is an ancestor (at any point) of the passed node.
27280      * @param {Node} node
27281      * @return {Boolean}
27282      */
27283     contains : function(node){
27284         return node.isAncestor(this);
27285     },
27286
27287     /**
27288      * Returns true if the passed node is an ancestor (at any point) of this node.
27289      * @param {Node} node
27290      * @return {Boolean}
27291      */
27292     isAncestor : function(node){
27293         var p = this.parentNode;
27294         while(p){
27295             if(p == node){
27296                 return true;
27297             }
27298             p = p.parentNode;
27299         }
27300         return false;
27301     },
27302
27303     toString : function(){
27304         return "[Node"+(this.id?" "+this.id:"")+"]";
27305     }
27306 });/*
27307  * Based on:
27308  * Ext JS Library 1.1.1
27309  * Copyright(c) 2006-2007, Ext JS, LLC.
27310  *
27311  * Originally Released Under LGPL - original licence link has changed is not relivant.
27312  *
27313  * Fork - LGPL
27314  * <script type="text/javascript">
27315  */
27316
27317
27318 /**
27319  * @class Roo.Shadow
27320  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27321  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27322  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27323  * @constructor
27324  * Create a new Shadow
27325  * @param {Object} config The config object
27326  */
27327 Roo.Shadow = function(config){
27328     Roo.apply(this, config);
27329     if(typeof this.mode != "string"){
27330         this.mode = this.defaultMode;
27331     }
27332     var o = this.offset, a = {h: 0};
27333     var rad = Math.floor(this.offset/2);
27334     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27335         case "drop":
27336             a.w = 0;
27337             a.l = a.t = o;
27338             a.t -= 1;
27339             if(Roo.isIE){
27340                 a.l -= this.offset + rad;
27341                 a.t -= this.offset + rad;
27342                 a.w -= rad;
27343                 a.h -= rad;
27344                 a.t += 1;
27345             }
27346         break;
27347         case "sides":
27348             a.w = (o*2);
27349             a.l = -o;
27350             a.t = o-1;
27351             if(Roo.isIE){
27352                 a.l -= (this.offset - rad);
27353                 a.t -= this.offset + rad;
27354                 a.l += 1;
27355                 a.w -= (this.offset - rad)*2;
27356                 a.w -= rad + 1;
27357                 a.h -= 1;
27358             }
27359         break;
27360         case "frame":
27361             a.w = a.h = (o*2);
27362             a.l = a.t = -o;
27363             a.t += 1;
27364             a.h -= 2;
27365             if(Roo.isIE){
27366                 a.l -= (this.offset - rad);
27367                 a.t -= (this.offset - rad);
27368                 a.l += 1;
27369                 a.w -= (this.offset + rad + 1);
27370                 a.h -= (this.offset + rad);
27371                 a.h += 1;
27372             }
27373         break;
27374     };
27375
27376     this.adjusts = a;
27377 };
27378
27379 Roo.Shadow.prototype = {
27380     /**
27381      * @cfg {String} mode
27382      * The shadow display mode.  Supports the following options:<br />
27383      * sides: Shadow displays on both sides and bottom only<br />
27384      * frame: Shadow displays equally on all four sides<br />
27385      * drop: Traditional bottom-right drop shadow (default)
27386      */
27387     mode: false,
27388     /**
27389      * @cfg {String} offset
27390      * The number of pixels to offset the shadow from the element (defaults to 4)
27391      */
27392     offset: 4,
27393
27394     // private
27395     defaultMode: "drop",
27396
27397     /**
27398      * Displays the shadow under the target element
27399      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27400      */
27401     show : function(target){
27402         target = Roo.get(target);
27403         if(!this.el){
27404             this.el = Roo.Shadow.Pool.pull();
27405             if(this.el.dom.nextSibling != target.dom){
27406                 this.el.insertBefore(target);
27407             }
27408         }
27409         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27410         if(Roo.isIE){
27411             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27412         }
27413         this.realign(
27414             target.getLeft(true),
27415             target.getTop(true),
27416             target.getWidth(),
27417             target.getHeight()
27418         );
27419         this.el.dom.style.display = "block";
27420     },
27421
27422     /**
27423      * Returns true if the shadow is visible, else false
27424      */
27425     isVisible : function(){
27426         return this.el ? true : false;  
27427     },
27428
27429     /**
27430      * Direct alignment when values are already available. Show must be called at least once before
27431      * calling this method to ensure it is initialized.
27432      * @param {Number} left The target element left position
27433      * @param {Number} top The target element top position
27434      * @param {Number} width The target element width
27435      * @param {Number} height The target element height
27436      */
27437     realign : function(l, t, w, h){
27438         if(!this.el){
27439             return;
27440         }
27441         var a = this.adjusts, d = this.el.dom, s = d.style;
27442         var iea = 0;
27443         s.left = (l+a.l)+"px";
27444         s.top = (t+a.t)+"px";
27445         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27446  
27447         if(s.width != sws || s.height != shs){
27448             s.width = sws;
27449             s.height = shs;
27450             if(!Roo.isIE){
27451                 var cn = d.childNodes;
27452                 var sww = Math.max(0, (sw-12))+"px";
27453                 cn[0].childNodes[1].style.width = sww;
27454                 cn[1].childNodes[1].style.width = sww;
27455                 cn[2].childNodes[1].style.width = sww;
27456                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27457             }
27458         }
27459     },
27460
27461     /**
27462      * Hides this shadow
27463      */
27464     hide : function(){
27465         if(this.el){
27466             this.el.dom.style.display = "none";
27467             Roo.Shadow.Pool.push(this.el);
27468             delete this.el;
27469         }
27470     },
27471
27472     /**
27473      * Adjust the z-index of this shadow
27474      * @param {Number} zindex The new z-index
27475      */
27476     setZIndex : function(z){
27477         this.zIndex = z;
27478         if(this.el){
27479             this.el.setStyle("z-index", z);
27480         }
27481     }
27482 };
27483
27484 // Private utility class that manages the internal Shadow cache
27485 Roo.Shadow.Pool = function(){
27486     var p = [];
27487     var markup = Roo.isIE ?
27488                  '<div class="x-ie-shadow"></div>' :
27489                  '<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>';
27490     return {
27491         pull : function(){
27492             var sh = p.shift();
27493             if(!sh){
27494                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27495                 sh.autoBoxAdjust = false;
27496             }
27497             return sh;
27498         },
27499
27500         push : function(sh){
27501             p.push(sh);
27502         }
27503     };
27504 }();/*
27505  * Based on:
27506  * Ext JS Library 1.1.1
27507  * Copyright(c) 2006-2007, Ext JS, LLC.
27508  *
27509  * Originally Released Under LGPL - original licence link has changed is not relivant.
27510  *
27511  * Fork - LGPL
27512  * <script type="text/javascript">
27513  */
27514
27515
27516 /**
27517  * @class Roo.SplitBar
27518  * @extends Roo.util.Observable
27519  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27520  * <br><br>
27521  * Usage:
27522  * <pre><code>
27523 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27524                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27525 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27526 split.minSize = 100;
27527 split.maxSize = 600;
27528 split.animate = true;
27529 split.on('moved', splitterMoved);
27530 </code></pre>
27531  * @constructor
27532  * Create a new SplitBar
27533  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27534  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27535  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27536  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27537                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27538                         position of the SplitBar).
27539  */
27540 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27541     
27542     /** @private */
27543     this.el = Roo.get(dragElement, true);
27544     this.el.dom.unselectable = "on";
27545     /** @private */
27546     this.resizingEl = Roo.get(resizingElement, true);
27547
27548     /**
27549      * @private
27550      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27551      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27552      * @type Number
27553      */
27554     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27555     
27556     /**
27557      * The minimum size of the resizing element. (Defaults to 0)
27558      * @type Number
27559      */
27560     this.minSize = 0;
27561     
27562     /**
27563      * The maximum size of the resizing element. (Defaults to 2000)
27564      * @type Number
27565      */
27566     this.maxSize = 2000;
27567     
27568     /**
27569      * Whether to animate the transition to the new size
27570      * @type Boolean
27571      */
27572     this.animate = false;
27573     
27574     /**
27575      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27576      * @type Boolean
27577      */
27578     this.useShim = false;
27579     
27580     /** @private */
27581     this.shim = null;
27582     
27583     if(!existingProxy){
27584         /** @private */
27585         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27586     }else{
27587         this.proxy = Roo.get(existingProxy).dom;
27588     }
27589     /** @private */
27590     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27591     
27592     /** @private */
27593     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27594     
27595     /** @private */
27596     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27597     
27598     /** @private */
27599     this.dragSpecs = {};
27600     
27601     /**
27602      * @private The adapter to use to positon and resize elements
27603      */
27604     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27605     this.adapter.init(this);
27606     
27607     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27608         /** @private */
27609         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27610         this.el.addClass("x-splitbar-h");
27611     }else{
27612         /** @private */
27613         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27614         this.el.addClass("x-splitbar-v");
27615     }
27616     
27617     this.addEvents({
27618         /**
27619          * @event resize
27620          * Fires when the splitter is moved (alias for {@link #event-moved})
27621          * @param {Roo.SplitBar} this
27622          * @param {Number} newSize the new width or height
27623          */
27624         "resize" : true,
27625         /**
27626          * @event moved
27627          * Fires when the splitter is moved
27628          * @param {Roo.SplitBar} this
27629          * @param {Number} newSize the new width or height
27630          */
27631         "moved" : true,
27632         /**
27633          * @event beforeresize
27634          * Fires before the splitter is dragged
27635          * @param {Roo.SplitBar} this
27636          */
27637         "beforeresize" : true,
27638
27639         "beforeapply" : true
27640     });
27641
27642     Roo.util.Observable.call(this);
27643 };
27644
27645 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27646     onStartProxyDrag : function(x, y){
27647         this.fireEvent("beforeresize", this);
27648         if(!this.overlay){
27649             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27650             o.unselectable();
27651             o.enableDisplayMode("block");
27652             // all splitbars share the same overlay
27653             Roo.SplitBar.prototype.overlay = o;
27654         }
27655         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27656         this.overlay.show();
27657         Roo.get(this.proxy).setDisplayed("block");
27658         var size = this.adapter.getElementSize(this);
27659         this.activeMinSize = this.getMinimumSize();;
27660         this.activeMaxSize = this.getMaximumSize();;
27661         var c1 = size - this.activeMinSize;
27662         var c2 = Math.max(this.activeMaxSize - size, 0);
27663         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27664             this.dd.resetConstraints();
27665             this.dd.setXConstraint(
27666                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27667                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27668             );
27669             this.dd.setYConstraint(0, 0);
27670         }else{
27671             this.dd.resetConstraints();
27672             this.dd.setXConstraint(0, 0);
27673             this.dd.setYConstraint(
27674                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27675                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27676             );
27677          }
27678         this.dragSpecs.startSize = size;
27679         this.dragSpecs.startPoint = [x, y];
27680         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27681     },
27682     
27683     /** 
27684      * @private Called after the drag operation by the DDProxy
27685      */
27686     onEndProxyDrag : function(e){
27687         Roo.get(this.proxy).setDisplayed(false);
27688         var endPoint = Roo.lib.Event.getXY(e);
27689         if(this.overlay){
27690             this.overlay.hide();
27691         }
27692         var newSize;
27693         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27694             newSize = this.dragSpecs.startSize + 
27695                 (this.placement == Roo.SplitBar.LEFT ?
27696                     endPoint[0] - this.dragSpecs.startPoint[0] :
27697                     this.dragSpecs.startPoint[0] - endPoint[0]
27698                 );
27699         }else{
27700             newSize = this.dragSpecs.startSize + 
27701                 (this.placement == Roo.SplitBar.TOP ?
27702                     endPoint[1] - this.dragSpecs.startPoint[1] :
27703                     this.dragSpecs.startPoint[1] - endPoint[1]
27704                 );
27705         }
27706         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27707         if(newSize != this.dragSpecs.startSize){
27708             if(this.fireEvent('beforeapply', this, newSize) !== false){
27709                 this.adapter.setElementSize(this, newSize);
27710                 this.fireEvent("moved", this, newSize);
27711                 this.fireEvent("resize", this, newSize);
27712             }
27713         }
27714     },
27715     
27716     /**
27717      * Get the adapter this SplitBar uses
27718      * @return The adapter object
27719      */
27720     getAdapter : function(){
27721         return this.adapter;
27722     },
27723     
27724     /**
27725      * Set the adapter this SplitBar uses
27726      * @param {Object} adapter A SplitBar adapter object
27727      */
27728     setAdapter : function(adapter){
27729         this.adapter = adapter;
27730         this.adapter.init(this);
27731     },
27732     
27733     /**
27734      * Gets the minimum size for the resizing element
27735      * @return {Number} The minimum size
27736      */
27737     getMinimumSize : function(){
27738         return this.minSize;
27739     },
27740     
27741     /**
27742      * Sets the minimum size for the resizing element
27743      * @param {Number} minSize The minimum size
27744      */
27745     setMinimumSize : function(minSize){
27746         this.minSize = minSize;
27747     },
27748     
27749     /**
27750      * Gets the maximum size for the resizing element
27751      * @return {Number} The maximum size
27752      */
27753     getMaximumSize : function(){
27754         return this.maxSize;
27755     },
27756     
27757     /**
27758      * Sets the maximum size for the resizing element
27759      * @param {Number} maxSize The maximum size
27760      */
27761     setMaximumSize : function(maxSize){
27762         this.maxSize = maxSize;
27763     },
27764     
27765     /**
27766      * Sets the initialize size for the resizing element
27767      * @param {Number} size The initial size
27768      */
27769     setCurrentSize : function(size){
27770         var oldAnimate = this.animate;
27771         this.animate = false;
27772         this.adapter.setElementSize(this, size);
27773         this.animate = oldAnimate;
27774     },
27775     
27776     /**
27777      * Destroy this splitbar. 
27778      * @param {Boolean} removeEl True to remove the element
27779      */
27780     destroy : function(removeEl){
27781         if(this.shim){
27782             this.shim.remove();
27783         }
27784         this.dd.unreg();
27785         this.proxy.parentNode.removeChild(this.proxy);
27786         if(removeEl){
27787             this.el.remove();
27788         }
27789     }
27790 });
27791
27792 /**
27793  * @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.
27794  */
27795 Roo.SplitBar.createProxy = function(dir){
27796     var proxy = new Roo.Element(document.createElement("div"));
27797     proxy.unselectable();
27798     var cls = 'x-splitbar-proxy';
27799     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27800     document.body.appendChild(proxy.dom);
27801     return proxy.dom;
27802 };
27803
27804 /** 
27805  * @class Roo.SplitBar.BasicLayoutAdapter
27806  * Default Adapter. It assumes the splitter and resizing element are not positioned
27807  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27808  */
27809 Roo.SplitBar.BasicLayoutAdapter = function(){
27810 };
27811
27812 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27813     // do nothing for now
27814     init : function(s){
27815     
27816     },
27817     /**
27818      * Called before drag operations to get the current size of the resizing element. 
27819      * @param {Roo.SplitBar} s The SplitBar using this adapter
27820      */
27821      getElementSize : function(s){
27822         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27823             return s.resizingEl.getWidth();
27824         }else{
27825             return s.resizingEl.getHeight();
27826         }
27827     },
27828     
27829     /**
27830      * Called after drag operations to set the size of the resizing element.
27831      * @param {Roo.SplitBar} s The SplitBar using this adapter
27832      * @param {Number} newSize The new size to set
27833      * @param {Function} onComplete A function to be invoked when resizing is complete
27834      */
27835     setElementSize : function(s, newSize, onComplete){
27836         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27837             if(!s.animate){
27838                 s.resizingEl.setWidth(newSize);
27839                 if(onComplete){
27840                     onComplete(s, newSize);
27841                 }
27842             }else{
27843                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27844             }
27845         }else{
27846             
27847             if(!s.animate){
27848                 s.resizingEl.setHeight(newSize);
27849                 if(onComplete){
27850                     onComplete(s, newSize);
27851                 }
27852             }else{
27853                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27854             }
27855         }
27856     }
27857 };
27858
27859 /** 
27860  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27861  * @extends Roo.SplitBar.BasicLayoutAdapter
27862  * Adapter that  moves the splitter element to align with the resized sizing element. 
27863  * Used with an absolute positioned SplitBar.
27864  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27865  * document.body, make sure you assign an id to the body element.
27866  */
27867 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27868     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27869     this.container = Roo.get(container);
27870 };
27871
27872 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27873     init : function(s){
27874         this.basic.init(s);
27875     },
27876     
27877     getElementSize : function(s){
27878         return this.basic.getElementSize(s);
27879     },
27880     
27881     setElementSize : function(s, newSize, onComplete){
27882         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27883     },
27884     
27885     moveSplitter : function(s){
27886         var yes = Roo.SplitBar;
27887         switch(s.placement){
27888             case yes.LEFT:
27889                 s.el.setX(s.resizingEl.getRight());
27890                 break;
27891             case yes.RIGHT:
27892                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27893                 break;
27894             case yes.TOP:
27895                 s.el.setY(s.resizingEl.getBottom());
27896                 break;
27897             case yes.BOTTOM:
27898                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27899                 break;
27900         }
27901     }
27902 };
27903
27904 /**
27905  * Orientation constant - Create a vertical SplitBar
27906  * @static
27907  * @type Number
27908  */
27909 Roo.SplitBar.VERTICAL = 1;
27910
27911 /**
27912  * Orientation constant - Create a horizontal SplitBar
27913  * @static
27914  * @type Number
27915  */
27916 Roo.SplitBar.HORIZONTAL = 2;
27917
27918 /**
27919  * Placement constant - The resizing element is to the left of the splitter element
27920  * @static
27921  * @type Number
27922  */
27923 Roo.SplitBar.LEFT = 1;
27924
27925 /**
27926  * Placement constant - The resizing element is to the right of the splitter element
27927  * @static
27928  * @type Number
27929  */
27930 Roo.SplitBar.RIGHT = 2;
27931
27932 /**
27933  * Placement constant - The resizing element is positioned above the splitter element
27934  * @static
27935  * @type Number
27936  */
27937 Roo.SplitBar.TOP = 3;
27938
27939 /**
27940  * Placement constant - The resizing element is positioned under splitter element
27941  * @static
27942  * @type Number
27943  */
27944 Roo.SplitBar.BOTTOM = 4;
27945 /*
27946  * Based on:
27947  * Ext JS Library 1.1.1
27948  * Copyright(c) 2006-2007, Ext JS, LLC.
27949  *
27950  * Originally Released Under LGPL - original licence link has changed is not relivant.
27951  *
27952  * Fork - LGPL
27953  * <script type="text/javascript">
27954  */
27955
27956 /**
27957  * @class Roo.View
27958  * @extends Roo.util.Observable
27959  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27960  * This class also supports single and multi selection modes. <br>
27961  * Create a data model bound view:
27962  <pre><code>
27963  var store = new Roo.data.Store(...);
27964
27965  var view = new Roo.View({
27966     el : "my-element",
27967     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27968  
27969     singleSelect: true,
27970     selectedClass: "ydataview-selected",
27971     store: store
27972  });
27973
27974  // listen for node click?
27975  view.on("click", function(vw, index, node, e){
27976  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27977  });
27978
27979  // load XML data
27980  dataModel.load("foobar.xml");
27981  </code></pre>
27982  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27983  * <br><br>
27984  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27985  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27986  * 
27987  * Note: old style constructor is still suported (container, template, config)
27988  * 
27989  * @constructor
27990  * Create a new View
27991  * @param {Object} config The config object
27992  * 
27993  */
27994 Roo.View = function(config, depreciated_tpl, depreciated_config){
27995     
27996     this.parent = false;
27997     
27998     if (typeof(depreciated_tpl) == 'undefined') {
27999         // new way.. - universal constructor.
28000         Roo.apply(this, config);
28001         this.el  = Roo.get(this.el);
28002     } else {
28003         // old format..
28004         this.el  = Roo.get(config);
28005         this.tpl = depreciated_tpl;
28006         Roo.apply(this, depreciated_config);
28007     }
28008     this.wrapEl  = this.el.wrap().wrap();
28009     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
28010     
28011     
28012     if(typeof(this.tpl) == "string"){
28013         this.tpl = new Roo.Template(this.tpl);
28014     } else {
28015         // support xtype ctors..
28016         this.tpl = new Roo.factory(this.tpl, Roo);
28017     }
28018     
28019     
28020     this.tpl.compile();
28021     
28022     /** @private */
28023     this.addEvents({
28024         /**
28025          * @event beforeclick
28026          * Fires before a click is processed. Returns false to cancel the default action.
28027          * @param {Roo.View} this
28028          * @param {Number} index The index of the target node
28029          * @param {HTMLElement} node The target node
28030          * @param {Roo.EventObject} e The raw event object
28031          */
28032             "beforeclick" : true,
28033         /**
28034          * @event click
28035          * Fires when a template node is clicked.
28036          * @param {Roo.View} this
28037          * @param {Number} index The index of the target node
28038          * @param {HTMLElement} node The target node
28039          * @param {Roo.EventObject} e The raw event object
28040          */
28041             "click" : true,
28042         /**
28043          * @event dblclick
28044          * Fires when a template node is double clicked.
28045          * @param {Roo.View} this
28046          * @param {Number} index The index of the target node
28047          * @param {HTMLElement} node The target node
28048          * @param {Roo.EventObject} e The raw event object
28049          */
28050             "dblclick" : true,
28051         /**
28052          * @event contextmenu
28053          * Fires when a template node is right clicked.
28054          * @param {Roo.View} this
28055          * @param {Number} index The index of the target node
28056          * @param {HTMLElement} node The target node
28057          * @param {Roo.EventObject} e The raw event object
28058          */
28059             "contextmenu" : true,
28060         /**
28061          * @event selectionchange
28062          * Fires when the selected nodes change.
28063          * @param {Roo.View} this
28064          * @param {Array} selections Array of the selected nodes
28065          */
28066             "selectionchange" : true,
28067     
28068         /**
28069          * @event beforeselect
28070          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28071          * @param {Roo.View} this
28072          * @param {HTMLElement} node The node to be selected
28073          * @param {Array} selections Array of currently selected nodes
28074          */
28075             "beforeselect" : true,
28076         /**
28077          * @event preparedata
28078          * Fires on every row to render, to allow you to change the data.
28079          * @param {Roo.View} this
28080          * @param {Object} data to be rendered (change this)
28081          */
28082           "preparedata" : true
28083           
28084           
28085         });
28086
28087
28088
28089     this.el.on({
28090         "click": this.onClick,
28091         "dblclick": this.onDblClick,
28092         "contextmenu": this.onContextMenu,
28093         scope:this
28094     });
28095
28096     this.selections = [];
28097     this.nodes = [];
28098     this.cmp = new Roo.CompositeElementLite([]);
28099     if(this.store){
28100         this.store = Roo.factory(this.store, Roo.data);
28101         this.setStore(this.store, true);
28102     }
28103     
28104     if ( this.footer && this.footer.xtype) {
28105            
28106          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28107         
28108         this.footer.dataSource = this.store;
28109         this.footer.container = fctr;
28110         this.footer = Roo.factory(this.footer, Roo);
28111         fctr.insertFirst(this.el);
28112         
28113         // this is a bit insane - as the paging toolbar seems to detach the el..
28114 //        dom.parentNode.parentNode.parentNode
28115          // they get detached?
28116     }
28117     
28118     
28119     Roo.View.superclass.constructor.call(this);
28120     
28121     
28122 };
28123
28124 Roo.extend(Roo.View, Roo.util.Observable, {
28125     
28126      /**
28127      * @cfg {Roo.data.Store} store Data store to load data from.
28128      */
28129     store : false,
28130     
28131     /**
28132      * @cfg {String|Roo.Element} el The container element.
28133      */
28134     el : '',
28135     
28136     /**
28137      * @cfg {String|Roo.Template} tpl The template used by this View 
28138      */
28139     tpl : false,
28140     /**
28141      * @cfg {String} dataName the named area of the template to use as the data area
28142      *                          Works with domtemplates roo-name="name"
28143      */
28144     dataName: false,
28145     /**
28146      * @cfg {String} selectedClass The css class to add to selected nodes
28147      */
28148     selectedClass : "x-view-selected",
28149      /**
28150      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28151      */
28152     emptyText : "",
28153     
28154     /**
28155      * @cfg {String} text to display on mask (default Loading)
28156      */
28157     mask : false,
28158     /**
28159      * @cfg {Boolean} multiSelect Allow multiple selection
28160      */
28161     multiSelect : false,
28162     /**
28163      * @cfg {Boolean} singleSelect Allow single selection
28164      */
28165     singleSelect:  false,
28166     
28167     /**
28168      * @cfg {Boolean} toggleSelect - selecting 
28169      */
28170     toggleSelect : false,
28171     
28172     /**
28173      * @cfg {Boolean} tickable - selecting 
28174      */
28175     tickable : false,
28176     
28177     /**
28178      * Returns the element this view is bound to.
28179      * @return {Roo.Element}
28180      */
28181     getEl : function(){
28182         return this.wrapEl;
28183     },
28184     
28185     
28186
28187     /**
28188      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28189      */
28190     refresh : function(){
28191         //Roo.log('refresh');
28192         var t = this.tpl;
28193         
28194         // if we are using something like 'domtemplate', then
28195         // the what gets used is:
28196         // t.applySubtemplate(NAME, data, wrapping data..)
28197         // the outer template then get' applied with
28198         //     the store 'extra data'
28199         // and the body get's added to the
28200         //      roo-name="data" node?
28201         //      <span class='roo-tpl-{name}'></span> ?????
28202         
28203         
28204         
28205         this.clearSelections();
28206         this.el.update("");
28207         var html = [];
28208         var records = this.store.getRange();
28209         if(records.length < 1) {
28210             
28211             // is this valid??  = should it render a template??
28212             
28213             this.el.update(this.emptyText);
28214             return;
28215         }
28216         var el = this.el;
28217         if (this.dataName) {
28218             this.el.update(t.apply(this.store.meta)); //????
28219             el = this.el.child('.roo-tpl-' + this.dataName);
28220         }
28221         
28222         for(var i = 0, len = records.length; i < len; i++){
28223             var data = this.prepareData(records[i].data, i, records[i]);
28224             this.fireEvent("preparedata", this, data, i, records[i]);
28225             
28226             var d = Roo.apply({}, data);
28227             
28228             if(this.tickable){
28229                 Roo.apply(d, {'roo-id' : Roo.id()});
28230                 
28231                 var _this = this;
28232             
28233                 Roo.each(this.parent.item, function(item){
28234                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28235                         return;
28236                     }
28237                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28238                 });
28239             }
28240             
28241             html[html.length] = Roo.util.Format.trim(
28242                 this.dataName ?
28243                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28244                     t.apply(d)
28245             );
28246         }
28247         
28248         
28249         
28250         el.update(html.join(""));
28251         this.nodes = el.dom.childNodes;
28252         this.updateIndexes(0);
28253     },
28254     
28255
28256     /**
28257      * Function to override to reformat the data that is sent to
28258      * the template for each node.
28259      * DEPRICATED - use the preparedata event handler.
28260      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28261      * a JSON object for an UpdateManager bound view).
28262      */
28263     prepareData : function(data, index, record)
28264     {
28265         this.fireEvent("preparedata", this, data, index, record);
28266         return data;
28267     },
28268
28269     onUpdate : function(ds, record){
28270         // Roo.log('on update');   
28271         this.clearSelections();
28272         var index = this.store.indexOf(record);
28273         var n = this.nodes[index];
28274         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28275         n.parentNode.removeChild(n);
28276         this.updateIndexes(index, index);
28277     },
28278
28279     
28280     
28281 // --------- FIXME     
28282     onAdd : function(ds, records, index)
28283     {
28284         //Roo.log(['on Add', ds, records, index] );        
28285         this.clearSelections();
28286         if(this.nodes.length == 0){
28287             this.refresh();
28288             return;
28289         }
28290         var n = this.nodes[index];
28291         for(var i = 0, len = records.length; i < len; i++){
28292             var d = this.prepareData(records[i].data, i, records[i]);
28293             if(n){
28294                 this.tpl.insertBefore(n, d);
28295             }else{
28296                 
28297                 this.tpl.append(this.el, d);
28298             }
28299         }
28300         this.updateIndexes(index);
28301     },
28302
28303     onRemove : function(ds, record, index){
28304        // Roo.log('onRemove');
28305         this.clearSelections();
28306         var el = this.dataName  ?
28307             this.el.child('.roo-tpl-' + this.dataName) :
28308             this.el; 
28309         
28310         el.dom.removeChild(this.nodes[index]);
28311         this.updateIndexes(index);
28312     },
28313
28314     /**
28315      * Refresh an individual node.
28316      * @param {Number} index
28317      */
28318     refreshNode : function(index){
28319         this.onUpdate(this.store, this.store.getAt(index));
28320     },
28321
28322     updateIndexes : function(startIndex, endIndex){
28323         var ns = this.nodes;
28324         startIndex = startIndex || 0;
28325         endIndex = endIndex || ns.length - 1;
28326         for(var i = startIndex; i <= endIndex; i++){
28327             ns[i].nodeIndex = i;
28328         }
28329     },
28330
28331     /**
28332      * Changes the data store this view uses and refresh the view.
28333      * @param {Store} store
28334      */
28335     setStore : function(store, initial){
28336         if(!initial && this.store){
28337             this.store.un("datachanged", this.refresh);
28338             this.store.un("add", this.onAdd);
28339             this.store.un("remove", this.onRemove);
28340             this.store.un("update", this.onUpdate);
28341             this.store.un("clear", this.refresh);
28342             this.store.un("beforeload", this.onBeforeLoad);
28343             this.store.un("load", this.onLoad);
28344             this.store.un("loadexception", this.onLoad);
28345         }
28346         if(store){
28347           
28348             store.on("datachanged", this.refresh, this);
28349             store.on("add", this.onAdd, this);
28350             store.on("remove", this.onRemove, this);
28351             store.on("update", this.onUpdate, this);
28352             store.on("clear", this.refresh, this);
28353             store.on("beforeload", this.onBeforeLoad, this);
28354             store.on("load", this.onLoad, this);
28355             store.on("loadexception", this.onLoad, this);
28356         }
28357         
28358         if(store){
28359             this.refresh();
28360         }
28361     },
28362     /**
28363      * onbeforeLoad - masks the loading area.
28364      *
28365      */
28366     onBeforeLoad : function(store,opts)
28367     {
28368          //Roo.log('onBeforeLoad');   
28369         if (!opts.add) {
28370             this.el.update("");
28371         }
28372         this.el.mask(this.mask ? this.mask : "Loading" ); 
28373     },
28374     onLoad : function ()
28375     {
28376         this.el.unmask();
28377     },
28378     
28379
28380     /**
28381      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28382      * @param {HTMLElement} node
28383      * @return {HTMLElement} The template node
28384      */
28385     findItemFromChild : function(node){
28386         var el = this.dataName  ?
28387             this.el.child('.roo-tpl-' + this.dataName,true) :
28388             this.el.dom; 
28389         
28390         if(!node || node.parentNode == el){
28391                     return node;
28392             }
28393             var p = node.parentNode;
28394             while(p && p != el){
28395             if(p.parentNode == el){
28396                 return p;
28397             }
28398             p = p.parentNode;
28399         }
28400             return null;
28401     },
28402
28403     /** @ignore */
28404     onClick : function(e){
28405         var item = this.findItemFromChild(e.getTarget());
28406         if(item){
28407             var index = this.indexOf(item);
28408             if(this.onItemClick(item, index, e) !== false){
28409                 this.fireEvent("click", this, index, item, e);
28410             }
28411         }else{
28412             this.clearSelections();
28413         }
28414     },
28415
28416     /** @ignore */
28417     onContextMenu : function(e){
28418         var item = this.findItemFromChild(e.getTarget());
28419         if(item){
28420             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28421         }
28422     },
28423
28424     /** @ignore */
28425     onDblClick : function(e){
28426         var item = this.findItemFromChild(e.getTarget());
28427         if(item){
28428             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28429         }
28430     },
28431
28432     onItemClick : function(item, index, e)
28433     {
28434         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28435             return false;
28436         }
28437         if (this.toggleSelect) {
28438             var m = this.isSelected(item) ? 'unselect' : 'select';
28439             //Roo.log(m);
28440             var _t = this;
28441             _t[m](item, true, false);
28442             return true;
28443         }
28444         if(this.multiSelect || this.singleSelect){
28445             if(this.multiSelect && e.shiftKey && this.lastSelection){
28446                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28447             }else{
28448                 this.select(item, this.multiSelect && e.ctrlKey);
28449                 this.lastSelection = item;
28450             }
28451             
28452             if(!this.tickable){
28453                 e.preventDefault();
28454             }
28455             
28456         }
28457         return true;
28458     },
28459
28460     /**
28461      * Get the number of selected nodes.
28462      * @return {Number}
28463      */
28464     getSelectionCount : function(){
28465         return this.selections.length;
28466     },
28467
28468     /**
28469      * Get the currently selected nodes.
28470      * @return {Array} An array of HTMLElements
28471      */
28472     getSelectedNodes : function(){
28473         return this.selections;
28474     },
28475
28476     /**
28477      * Get the indexes of the selected nodes.
28478      * @return {Array}
28479      */
28480     getSelectedIndexes : function(){
28481         var indexes = [], s = this.selections;
28482         for(var i = 0, len = s.length; i < len; i++){
28483             indexes.push(s[i].nodeIndex);
28484         }
28485         return indexes;
28486     },
28487
28488     /**
28489      * Clear all selections
28490      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28491      */
28492     clearSelections : function(suppressEvent){
28493         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28494             this.cmp.elements = this.selections;
28495             this.cmp.removeClass(this.selectedClass);
28496             this.selections = [];
28497             if(!suppressEvent){
28498                 this.fireEvent("selectionchange", this, this.selections);
28499             }
28500         }
28501     },
28502
28503     /**
28504      * Returns true if the passed node is selected
28505      * @param {HTMLElement/Number} node The node or node index
28506      * @return {Boolean}
28507      */
28508     isSelected : function(node){
28509         var s = this.selections;
28510         if(s.length < 1){
28511             return false;
28512         }
28513         node = this.getNode(node);
28514         return s.indexOf(node) !== -1;
28515     },
28516
28517     /**
28518      * Selects nodes.
28519      * @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
28520      * @param {Boolean} keepExisting (optional) true to keep existing selections
28521      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28522      */
28523     select : function(nodeInfo, keepExisting, suppressEvent){
28524         if(nodeInfo instanceof Array){
28525             if(!keepExisting){
28526                 this.clearSelections(true);
28527             }
28528             for(var i = 0, len = nodeInfo.length; i < len; i++){
28529                 this.select(nodeInfo[i], true, true);
28530             }
28531             return;
28532         } 
28533         var node = this.getNode(nodeInfo);
28534         if(!node || this.isSelected(node)){
28535             return; // already selected.
28536         }
28537         if(!keepExisting){
28538             this.clearSelections(true);
28539         }
28540         
28541         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28542             Roo.fly(node).addClass(this.selectedClass);
28543             this.selections.push(node);
28544             if(!suppressEvent){
28545                 this.fireEvent("selectionchange", this, this.selections);
28546             }
28547         }
28548         
28549         
28550     },
28551       /**
28552      * Unselects nodes.
28553      * @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
28554      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28555      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28556      */
28557     unselect : function(nodeInfo, keepExisting, suppressEvent)
28558     {
28559         if(nodeInfo instanceof Array){
28560             Roo.each(this.selections, function(s) {
28561                 this.unselect(s, nodeInfo);
28562             }, this);
28563             return;
28564         }
28565         var node = this.getNode(nodeInfo);
28566         if(!node || !this.isSelected(node)){
28567             //Roo.log("not selected");
28568             return; // not selected.
28569         }
28570         // fireevent???
28571         var ns = [];
28572         Roo.each(this.selections, function(s) {
28573             if (s == node ) {
28574                 Roo.fly(node).removeClass(this.selectedClass);
28575
28576                 return;
28577             }
28578             ns.push(s);
28579         },this);
28580         
28581         this.selections= ns;
28582         this.fireEvent("selectionchange", this, this.selections);
28583     },
28584
28585     /**
28586      * Gets a template node.
28587      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28588      * @return {HTMLElement} The node or null if it wasn't found
28589      */
28590     getNode : function(nodeInfo){
28591         if(typeof nodeInfo == "string"){
28592             return document.getElementById(nodeInfo);
28593         }else if(typeof nodeInfo == "number"){
28594             return this.nodes[nodeInfo];
28595         }
28596         return nodeInfo;
28597     },
28598
28599     /**
28600      * Gets a range template nodes.
28601      * @param {Number} startIndex
28602      * @param {Number} endIndex
28603      * @return {Array} An array of nodes
28604      */
28605     getNodes : function(start, end){
28606         var ns = this.nodes;
28607         start = start || 0;
28608         end = typeof end == "undefined" ? ns.length - 1 : end;
28609         var nodes = [];
28610         if(start <= end){
28611             for(var i = start; i <= end; i++){
28612                 nodes.push(ns[i]);
28613             }
28614         } else{
28615             for(var i = start; i >= end; i--){
28616                 nodes.push(ns[i]);
28617             }
28618         }
28619         return nodes;
28620     },
28621
28622     /**
28623      * Finds the index of the passed node
28624      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28625      * @return {Number} The index of the node or -1
28626      */
28627     indexOf : function(node){
28628         node = this.getNode(node);
28629         if(typeof node.nodeIndex == "number"){
28630             return node.nodeIndex;
28631         }
28632         var ns = this.nodes;
28633         for(var i = 0, len = ns.length; i < len; i++){
28634             if(ns[i] == node){
28635                 return i;
28636             }
28637         }
28638         return -1;
28639     }
28640 });
28641 /*
28642  * Based on:
28643  * Ext JS Library 1.1.1
28644  * Copyright(c) 2006-2007, Ext JS, LLC.
28645  *
28646  * Originally Released Under LGPL - original licence link has changed is not relivant.
28647  *
28648  * Fork - LGPL
28649  * <script type="text/javascript">
28650  */
28651
28652 /**
28653  * @class Roo.JsonView
28654  * @extends Roo.View
28655  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28656 <pre><code>
28657 var view = new Roo.JsonView({
28658     container: "my-element",
28659     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28660     multiSelect: true, 
28661     jsonRoot: "data" 
28662 });
28663
28664 // listen for node click?
28665 view.on("click", function(vw, index, node, e){
28666     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28667 });
28668
28669 // direct load of JSON data
28670 view.load("foobar.php");
28671
28672 // Example from my blog list
28673 var tpl = new Roo.Template(
28674     '&lt;div class="entry"&gt;' +
28675     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28676     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28677     "&lt;/div&gt;&lt;hr /&gt;"
28678 );
28679
28680 var moreView = new Roo.JsonView({
28681     container :  "entry-list", 
28682     template : tpl,
28683     jsonRoot: "posts"
28684 });
28685 moreView.on("beforerender", this.sortEntries, this);
28686 moreView.load({
28687     url: "/blog/get-posts.php",
28688     params: "allposts=true",
28689     text: "Loading Blog Entries..."
28690 });
28691 </code></pre>
28692
28693 * Note: old code is supported with arguments : (container, template, config)
28694
28695
28696  * @constructor
28697  * Create a new JsonView
28698  * 
28699  * @param {Object} config The config object
28700  * 
28701  */
28702 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28703     
28704     
28705     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28706
28707     var um = this.el.getUpdateManager();
28708     um.setRenderer(this);
28709     um.on("update", this.onLoad, this);
28710     um.on("failure", this.onLoadException, this);
28711
28712     /**
28713      * @event beforerender
28714      * Fires before rendering of the downloaded JSON data.
28715      * @param {Roo.JsonView} this
28716      * @param {Object} data The JSON data loaded
28717      */
28718     /**
28719      * @event load
28720      * Fires when data is loaded.
28721      * @param {Roo.JsonView} this
28722      * @param {Object} data The JSON data loaded
28723      * @param {Object} response The raw Connect response object
28724      */
28725     /**
28726      * @event loadexception
28727      * Fires when loading fails.
28728      * @param {Roo.JsonView} this
28729      * @param {Object} response The raw Connect response object
28730      */
28731     this.addEvents({
28732         'beforerender' : true,
28733         'load' : true,
28734         'loadexception' : true
28735     });
28736 };
28737 Roo.extend(Roo.JsonView, Roo.View, {
28738     /**
28739      * @type {String} The root property in the loaded JSON object that contains the data
28740      */
28741     jsonRoot : "",
28742
28743     /**
28744      * Refreshes the view.
28745      */
28746     refresh : function(){
28747         this.clearSelections();
28748         this.el.update("");
28749         var html = [];
28750         var o = this.jsonData;
28751         if(o && o.length > 0){
28752             for(var i = 0, len = o.length; i < len; i++){
28753                 var data = this.prepareData(o[i], i, o);
28754                 html[html.length] = this.tpl.apply(data);
28755             }
28756         }else{
28757             html.push(this.emptyText);
28758         }
28759         this.el.update(html.join(""));
28760         this.nodes = this.el.dom.childNodes;
28761         this.updateIndexes(0);
28762     },
28763
28764     /**
28765      * 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.
28766      * @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:
28767      <pre><code>
28768      view.load({
28769          url: "your-url.php",
28770          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28771          callback: yourFunction,
28772          scope: yourObject, //(optional scope)
28773          discardUrl: false,
28774          nocache: false,
28775          text: "Loading...",
28776          timeout: 30,
28777          scripts: false
28778      });
28779      </code></pre>
28780      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28781      * 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.
28782      * @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}
28783      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28784      * @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.
28785      */
28786     load : function(){
28787         var um = this.el.getUpdateManager();
28788         um.update.apply(um, arguments);
28789     },
28790
28791     // note - render is a standard framework call...
28792     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28793     render : function(el, response){
28794         
28795         this.clearSelections();
28796         this.el.update("");
28797         var o;
28798         try{
28799             if (response != '') {
28800                 o = Roo.util.JSON.decode(response.responseText);
28801                 if(this.jsonRoot){
28802                     
28803                     o = o[this.jsonRoot];
28804                 }
28805             }
28806         } catch(e){
28807         }
28808         /**
28809          * The current JSON data or null
28810          */
28811         this.jsonData = o;
28812         this.beforeRender();
28813         this.refresh();
28814     },
28815
28816 /**
28817  * Get the number of records in the current JSON dataset
28818  * @return {Number}
28819  */
28820     getCount : function(){
28821         return this.jsonData ? this.jsonData.length : 0;
28822     },
28823
28824 /**
28825  * Returns the JSON object for the specified node(s)
28826  * @param {HTMLElement/Array} node The node or an array of nodes
28827  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28828  * you get the JSON object for the node
28829  */
28830     getNodeData : function(node){
28831         if(node instanceof Array){
28832             var data = [];
28833             for(var i = 0, len = node.length; i < len; i++){
28834                 data.push(this.getNodeData(node[i]));
28835             }
28836             return data;
28837         }
28838         return this.jsonData[this.indexOf(node)] || null;
28839     },
28840
28841     beforeRender : function(){
28842         this.snapshot = this.jsonData;
28843         if(this.sortInfo){
28844             this.sort.apply(this, this.sortInfo);
28845         }
28846         this.fireEvent("beforerender", this, this.jsonData);
28847     },
28848
28849     onLoad : function(el, o){
28850         this.fireEvent("load", this, this.jsonData, o);
28851     },
28852
28853     onLoadException : function(el, o){
28854         this.fireEvent("loadexception", this, o);
28855     },
28856
28857 /**
28858  * Filter the data by a specific property.
28859  * @param {String} property A property on your JSON objects
28860  * @param {String/RegExp} value Either string that the property values
28861  * should start with, or a RegExp to test against the property
28862  */
28863     filter : function(property, value){
28864         if(this.jsonData){
28865             var data = [];
28866             var ss = this.snapshot;
28867             if(typeof value == "string"){
28868                 var vlen = value.length;
28869                 if(vlen == 0){
28870                     this.clearFilter();
28871                     return;
28872                 }
28873                 value = value.toLowerCase();
28874                 for(var i = 0, len = ss.length; i < len; i++){
28875                     var o = ss[i];
28876                     if(o[property].substr(0, vlen).toLowerCase() == value){
28877                         data.push(o);
28878                     }
28879                 }
28880             } else if(value.exec){ // regex?
28881                 for(var i = 0, len = ss.length; i < len; i++){
28882                     var o = ss[i];
28883                     if(value.test(o[property])){
28884                         data.push(o);
28885                     }
28886                 }
28887             } else{
28888                 return;
28889             }
28890             this.jsonData = data;
28891             this.refresh();
28892         }
28893     },
28894
28895 /**
28896  * Filter by a function. The passed function will be called with each
28897  * object in the current dataset. If the function returns true the value is kept,
28898  * otherwise it is filtered.
28899  * @param {Function} fn
28900  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28901  */
28902     filterBy : function(fn, scope){
28903         if(this.jsonData){
28904             var data = [];
28905             var ss = this.snapshot;
28906             for(var i = 0, len = ss.length; i < len; i++){
28907                 var o = ss[i];
28908                 if(fn.call(scope || this, o)){
28909                     data.push(o);
28910                 }
28911             }
28912             this.jsonData = data;
28913             this.refresh();
28914         }
28915     },
28916
28917 /**
28918  * Clears the current filter.
28919  */
28920     clearFilter : function(){
28921         if(this.snapshot && this.jsonData != this.snapshot){
28922             this.jsonData = this.snapshot;
28923             this.refresh();
28924         }
28925     },
28926
28927
28928 /**
28929  * Sorts the data for this view and refreshes it.
28930  * @param {String} property A property on your JSON objects to sort on
28931  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28932  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28933  */
28934     sort : function(property, dir, sortType){
28935         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28936         if(this.jsonData){
28937             var p = property;
28938             var dsc = dir && dir.toLowerCase() == "desc";
28939             var f = function(o1, o2){
28940                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28941                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28942                 ;
28943                 if(v1 < v2){
28944                     return dsc ? +1 : -1;
28945                 } else if(v1 > v2){
28946                     return dsc ? -1 : +1;
28947                 } else{
28948                     return 0;
28949                 }
28950             };
28951             this.jsonData.sort(f);
28952             this.refresh();
28953             if(this.jsonData != this.snapshot){
28954                 this.snapshot.sort(f);
28955             }
28956         }
28957     }
28958 });/*
28959  * Based on:
28960  * Ext JS Library 1.1.1
28961  * Copyright(c) 2006-2007, Ext JS, LLC.
28962  *
28963  * Originally Released Under LGPL - original licence link has changed is not relivant.
28964  *
28965  * Fork - LGPL
28966  * <script type="text/javascript">
28967  */
28968  
28969
28970 /**
28971  * @class Roo.ColorPalette
28972  * @extends Roo.Component
28973  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28974  * Here's an example of typical usage:
28975  * <pre><code>
28976 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28977 cp.render('my-div');
28978
28979 cp.on('select', function(palette, selColor){
28980     // do something with selColor
28981 });
28982 </code></pre>
28983  * @constructor
28984  * Create a new ColorPalette
28985  * @param {Object} config The config object
28986  */
28987 Roo.ColorPalette = function(config){
28988     Roo.ColorPalette.superclass.constructor.call(this, config);
28989     this.addEvents({
28990         /**
28991              * @event select
28992              * Fires when a color is selected
28993              * @param {ColorPalette} this
28994              * @param {String} color The 6-digit color hex code (without the # symbol)
28995              */
28996         select: true
28997     });
28998
28999     if(this.handler){
29000         this.on("select", this.handler, this.scope, true);
29001     }
29002 };
29003 Roo.extend(Roo.ColorPalette, Roo.Component, {
29004     /**
29005      * @cfg {String} itemCls
29006      * The CSS class to apply to the containing element (defaults to "x-color-palette")
29007      */
29008     itemCls : "x-color-palette",
29009     /**
29010      * @cfg {String} value
29011      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
29012      * the hex codes are case-sensitive.
29013      */
29014     value : null,
29015     clickEvent:'click',
29016     // private
29017     ctype: "Roo.ColorPalette",
29018
29019     /**
29020      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29021      */
29022     allowReselect : false,
29023
29024     /**
29025      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29026      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29027      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29028      * of colors with the width setting until the box is symmetrical.</p>
29029      * <p>You can override individual colors if needed:</p>
29030      * <pre><code>
29031 var cp = new Roo.ColorPalette();
29032 cp.colors[0] = "FF0000";  // change the first box to red
29033 </code></pre>
29034
29035 Or you can provide a custom array of your own for complete control:
29036 <pre><code>
29037 var cp = new Roo.ColorPalette();
29038 cp.colors = ["000000", "993300", "333300"];
29039 </code></pre>
29040      * @type Array
29041      */
29042     colors : [
29043         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29044         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29045         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29046         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29047         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29048     ],
29049
29050     // private
29051     onRender : function(container, position){
29052         var t = new Roo.MasterTemplate(
29053             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29054         );
29055         var c = this.colors;
29056         for(var i = 0, len = c.length; i < len; i++){
29057             t.add([c[i]]);
29058         }
29059         var el = document.createElement("div");
29060         el.className = this.itemCls;
29061         t.overwrite(el);
29062         container.dom.insertBefore(el, position);
29063         this.el = Roo.get(el);
29064         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29065         if(this.clickEvent != 'click'){
29066             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29067         }
29068     },
29069
29070     // private
29071     afterRender : function(){
29072         Roo.ColorPalette.superclass.afterRender.call(this);
29073         if(this.value){
29074             var s = this.value;
29075             this.value = null;
29076             this.select(s);
29077         }
29078     },
29079
29080     // private
29081     handleClick : function(e, t){
29082         e.preventDefault();
29083         if(!this.disabled){
29084             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29085             this.select(c.toUpperCase());
29086         }
29087     },
29088
29089     /**
29090      * Selects the specified color in the palette (fires the select event)
29091      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29092      */
29093     select : function(color){
29094         color = color.replace("#", "");
29095         if(color != this.value || this.allowReselect){
29096             var el = this.el;
29097             if(this.value){
29098                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29099             }
29100             el.child("a.color-"+color).addClass("x-color-palette-sel");
29101             this.value = color;
29102             this.fireEvent("select", this, color);
29103         }
29104     }
29105 });/*
29106  * Based on:
29107  * Ext JS Library 1.1.1
29108  * Copyright(c) 2006-2007, Ext JS, LLC.
29109  *
29110  * Originally Released Under LGPL - original licence link has changed is not relivant.
29111  *
29112  * Fork - LGPL
29113  * <script type="text/javascript">
29114  */
29115  
29116 /**
29117  * @class Roo.DatePicker
29118  * @extends Roo.Component
29119  * Simple date picker class.
29120  * @constructor
29121  * Create a new DatePicker
29122  * @param {Object} config The config object
29123  */
29124 Roo.DatePicker = function(config){
29125     Roo.DatePicker.superclass.constructor.call(this, config);
29126
29127     this.value = config && config.value ?
29128                  config.value.clearTime() : new Date().clearTime();
29129
29130     this.addEvents({
29131         /**
29132              * @event select
29133              * Fires when a date is selected
29134              * @param {DatePicker} this
29135              * @param {Date} date The selected date
29136              */
29137         'select': true,
29138         /**
29139              * @event monthchange
29140              * Fires when the displayed month changes 
29141              * @param {DatePicker} this
29142              * @param {Date} date The selected month
29143              */
29144         'monthchange': true
29145     });
29146
29147     if(this.handler){
29148         this.on("select", this.handler,  this.scope || this);
29149     }
29150     // build the disabledDatesRE
29151     if(!this.disabledDatesRE && this.disabledDates){
29152         var dd = this.disabledDates;
29153         var re = "(?:";
29154         for(var i = 0; i < dd.length; i++){
29155             re += dd[i];
29156             if(i != dd.length-1) {
29157                 re += "|";
29158             }
29159         }
29160         this.disabledDatesRE = new RegExp(re + ")");
29161     }
29162 };
29163
29164 Roo.extend(Roo.DatePicker, Roo.Component, {
29165     /**
29166      * @cfg {String} todayText
29167      * The text to display on the button that selects the current date (defaults to "Today")
29168      */
29169     todayText : "Today",
29170     /**
29171      * @cfg {String} okText
29172      * The text to display on the ok button
29173      */
29174     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29175     /**
29176      * @cfg {String} cancelText
29177      * The text to display on the cancel button
29178      */
29179     cancelText : "Cancel",
29180     /**
29181      * @cfg {String} todayTip
29182      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29183      */
29184     todayTip : "{0} (Spacebar)",
29185     /**
29186      * @cfg {Date} minDate
29187      * Minimum allowable date (JavaScript date object, defaults to null)
29188      */
29189     minDate : null,
29190     /**
29191      * @cfg {Date} maxDate
29192      * Maximum allowable date (JavaScript date object, defaults to null)
29193      */
29194     maxDate : null,
29195     /**
29196      * @cfg {String} minText
29197      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29198      */
29199     minText : "This date is before the minimum date",
29200     /**
29201      * @cfg {String} maxText
29202      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29203      */
29204     maxText : "This date is after the maximum date",
29205     /**
29206      * @cfg {String} format
29207      * The default date format string which can be overriden for localization support.  The format must be
29208      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29209      */
29210     format : "m/d/y",
29211     /**
29212      * @cfg {Array} disabledDays
29213      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29214      */
29215     disabledDays : null,
29216     /**
29217      * @cfg {String} disabledDaysText
29218      * The tooltip to display when the date falls on a disabled day (defaults to "")
29219      */
29220     disabledDaysText : "",
29221     /**
29222      * @cfg {RegExp} disabledDatesRE
29223      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29224      */
29225     disabledDatesRE : null,
29226     /**
29227      * @cfg {String} disabledDatesText
29228      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29229      */
29230     disabledDatesText : "",
29231     /**
29232      * @cfg {Boolean} constrainToViewport
29233      * True to constrain the date picker to the viewport (defaults to true)
29234      */
29235     constrainToViewport : true,
29236     /**
29237      * @cfg {Array} monthNames
29238      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29239      */
29240     monthNames : Date.monthNames,
29241     /**
29242      * @cfg {Array} dayNames
29243      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29244      */
29245     dayNames : Date.dayNames,
29246     /**
29247      * @cfg {String} nextText
29248      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29249      */
29250     nextText: 'Next Month (Control+Right)',
29251     /**
29252      * @cfg {String} prevText
29253      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29254      */
29255     prevText: 'Previous Month (Control+Left)',
29256     /**
29257      * @cfg {String} monthYearText
29258      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29259      */
29260     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29261     /**
29262      * @cfg {Number} startDay
29263      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29264      */
29265     startDay : 0,
29266     /**
29267      * @cfg {Bool} showClear
29268      * Show a clear button (usefull for date form elements that can be blank.)
29269      */
29270     
29271     showClear: false,
29272     
29273     /**
29274      * Sets the value of the date field
29275      * @param {Date} value The date to set
29276      */
29277     setValue : function(value){
29278         var old = this.value;
29279         
29280         if (typeof(value) == 'string') {
29281          
29282             value = Date.parseDate(value, this.format);
29283         }
29284         if (!value) {
29285             value = new Date();
29286         }
29287         
29288         this.value = value.clearTime(true);
29289         if(this.el){
29290             this.update(this.value);
29291         }
29292     },
29293
29294     /**
29295      * Gets the current selected value of the date field
29296      * @return {Date} The selected date
29297      */
29298     getValue : function(){
29299         return this.value;
29300     },
29301
29302     // private
29303     focus : function(){
29304         if(this.el){
29305             this.update(this.activeDate);
29306         }
29307     },
29308
29309     // privateval
29310     onRender : function(container, position){
29311         
29312         var m = [
29313              '<table cellspacing="0">',
29314                 '<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>',
29315                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29316         var dn = this.dayNames;
29317         for(var i = 0; i < 7; i++){
29318             var d = this.startDay+i;
29319             if(d > 6){
29320                 d = d-7;
29321             }
29322             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29323         }
29324         m[m.length] = "</tr></thead><tbody><tr>";
29325         for(var i = 0; i < 42; i++) {
29326             if(i % 7 == 0 && i != 0){
29327                 m[m.length] = "</tr><tr>";
29328             }
29329             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29330         }
29331         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29332             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29333
29334         var el = document.createElement("div");
29335         el.className = "x-date-picker";
29336         el.innerHTML = m.join("");
29337
29338         container.dom.insertBefore(el, position);
29339
29340         this.el = Roo.get(el);
29341         this.eventEl = Roo.get(el.firstChild);
29342
29343         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29344             handler: this.showPrevMonth,
29345             scope: this,
29346             preventDefault:true,
29347             stopDefault:true
29348         });
29349
29350         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29351             handler: this.showNextMonth,
29352             scope: this,
29353             preventDefault:true,
29354             stopDefault:true
29355         });
29356
29357         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29358
29359         this.monthPicker = this.el.down('div.x-date-mp');
29360         this.monthPicker.enableDisplayMode('block');
29361         
29362         var kn = new Roo.KeyNav(this.eventEl, {
29363             "left" : function(e){
29364                 e.ctrlKey ?
29365                     this.showPrevMonth() :
29366                     this.update(this.activeDate.add("d", -1));
29367             },
29368
29369             "right" : function(e){
29370                 e.ctrlKey ?
29371                     this.showNextMonth() :
29372                     this.update(this.activeDate.add("d", 1));
29373             },
29374
29375             "up" : function(e){
29376                 e.ctrlKey ?
29377                     this.showNextYear() :
29378                     this.update(this.activeDate.add("d", -7));
29379             },
29380
29381             "down" : function(e){
29382                 e.ctrlKey ?
29383                     this.showPrevYear() :
29384                     this.update(this.activeDate.add("d", 7));
29385             },
29386
29387             "pageUp" : function(e){
29388                 this.showNextMonth();
29389             },
29390
29391             "pageDown" : function(e){
29392                 this.showPrevMonth();
29393             },
29394
29395             "enter" : function(e){
29396                 e.stopPropagation();
29397                 return true;
29398             },
29399
29400             scope : this
29401         });
29402
29403         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29404
29405         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29406
29407         this.el.unselectable();
29408         
29409         this.cells = this.el.select("table.x-date-inner tbody td");
29410         this.textNodes = this.el.query("table.x-date-inner tbody span");
29411
29412         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29413             text: "&#160;",
29414             tooltip: this.monthYearText
29415         });
29416
29417         this.mbtn.on('click', this.showMonthPicker, this);
29418         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29419
29420
29421         var today = (new Date()).dateFormat(this.format);
29422         
29423         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29424         if (this.showClear) {
29425             baseTb.add( new Roo.Toolbar.Fill());
29426         }
29427         baseTb.add({
29428             text: String.format(this.todayText, today),
29429             tooltip: String.format(this.todayTip, today),
29430             handler: this.selectToday,
29431             scope: this
29432         });
29433         
29434         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29435             
29436         //});
29437         if (this.showClear) {
29438             
29439             baseTb.add( new Roo.Toolbar.Fill());
29440             baseTb.add({
29441                 text: '&#160;',
29442                 cls: 'x-btn-icon x-btn-clear',
29443                 handler: function() {
29444                     //this.value = '';
29445                     this.fireEvent("select", this, '');
29446                 },
29447                 scope: this
29448             });
29449         }
29450         
29451         
29452         if(Roo.isIE){
29453             this.el.repaint();
29454         }
29455         this.update(this.value);
29456     },
29457
29458     createMonthPicker : function(){
29459         if(!this.monthPicker.dom.firstChild){
29460             var buf = ['<table border="0" cellspacing="0">'];
29461             for(var i = 0; i < 6; i++){
29462                 buf.push(
29463                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29464                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29465                     i == 0 ?
29466                     '<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>' :
29467                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29468                 );
29469             }
29470             buf.push(
29471                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29472                     this.okText,
29473                     '</button><button type="button" class="x-date-mp-cancel">',
29474                     this.cancelText,
29475                     '</button></td></tr>',
29476                 '</table>'
29477             );
29478             this.monthPicker.update(buf.join(''));
29479             this.monthPicker.on('click', this.onMonthClick, this);
29480             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29481
29482             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29483             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29484
29485             this.mpMonths.each(function(m, a, i){
29486                 i += 1;
29487                 if((i%2) == 0){
29488                     m.dom.xmonth = 5 + Math.round(i * .5);
29489                 }else{
29490                     m.dom.xmonth = Math.round((i-1) * .5);
29491                 }
29492             });
29493         }
29494     },
29495
29496     showMonthPicker : function(){
29497         this.createMonthPicker();
29498         var size = this.el.getSize();
29499         this.monthPicker.setSize(size);
29500         this.monthPicker.child('table').setSize(size);
29501
29502         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29503         this.updateMPMonth(this.mpSelMonth);
29504         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29505         this.updateMPYear(this.mpSelYear);
29506
29507         this.monthPicker.slideIn('t', {duration:.2});
29508     },
29509
29510     updateMPYear : function(y){
29511         this.mpyear = y;
29512         var ys = this.mpYears.elements;
29513         for(var i = 1; i <= 10; i++){
29514             var td = ys[i-1], y2;
29515             if((i%2) == 0){
29516                 y2 = y + Math.round(i * .5);
29517                 td.firstChild.innerHTML = y2;
29518                 td.xyear = y2;
29519             }else{
29520                 y2 = y - (5-Math.round(i * .5));
29521                 td.firstChild.innerHTML = y2;
29522                 td.xyear = y2;
29523             }
29524             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29525         }
29526     },
29527
29528     updateMPMonth : function(sm){
29529         this.mpMonths.each(function(m, a, i){
29530             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29531         });
29532     },
29533
29534     selectMPMonth: function(m){
29535         
29536     },
29537
29538     onMonthClick : function(e, t){
29539         e.stopEvent();
29540         var el = new Roo.Element(t), pn;
29541         if(el.is('button.x-date-mp-cancel')){
29542             this.hideMonthPicker();
29543         }
29544         else if(el.is('button.x-date-mp-ok')){
29545             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29546             this.hideMonthPicker();
29547         }
29548         else if(pn = el.up('td.x-date-mp-month', 2)){
29549             this.mpMonths.removeClass('x-date-mp-sel');
29550             pn.addClass('x-date-mp-sel');
29551             this.mpSelMonth = pn.dom.xmonth;
29552         }
29553         else if(pn = el.up('td.x-date-mp-year', 2)){
29554             this.mpYears.removeClass('x-date-mp-sel');
29555             pn.addClass('x-date-mp-sel');
29556             this.mpSelYear = pn.dom.xyear;
29557         }
29558         else if(el.is('a.x-date-mp-prev')){
29559             this.updateMPYear(this.mpyear-10);
29560         }
29561         else if(el.is('a.x-date-mp-next')){
29562             this.updateMPYear(this.mpyear+10);
29563         }
29564     },
29565
29566     onMonthDblClick : function(e, t){
29567         e.stopEvent();
29568         var el = new Roo.Element(t), pn;
29569         if(pn = el.up('td.x-date-mp-month', 2)){
29570             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29571             this.hideMonthPicker();
29572         }
29573         else if(pn = el.up('td.x-date-mp-year', 2)){
29574             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29575             this.hideMonthPicker();
29576         }
29577     },
29578
29579     hideMonthPicker : function(disableAnim){
29580         if(this.monthPicker){
29581             if(disableAnim === true){
29582                 this.monthPicker.hide();
29583             }else{
29584                 this.monthPicker.slideOut('t', {duration:.2});
29585             }
29586         }
29587     },
29588
29589     // private
29590     showPrevMonth : function(e){
29591         this.update(this.activeDate.add("mo", -1));
29592     },
29593
29594     // private
29595     showNextMonth : function(e){
29596         this.update(this.activeDate.add("mo", 1));
29597     },
29598
29599     // private
29600     showPrevYear : function(){
29601         this.update(this.activeDate.add("y", -1));
29602     },
29603
29604     // private
29605     showNextYear : function(){
29606         this.update(this.activeDate.add("y", 1));
29607     },
29608
29609     // private
29610     handleMouseWheel : function(e){
29611         var delta = e.getWheelDelta();
29612         if(delta > 0){
29613             this.showPrevMonth();
29614             e.stopEvent();
29615         } else if(delta < 0){
29616             this.showNextMonth();
29617             e.stopEvent();
29618         }
29619     },
29620
29621     // private
29622     handleDateClick : function(e, t){
29623         e.stopEvent();
29624         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29625             this.setValue(new Date(t.dateValue));
29626             this.fireEvent("select", this, this.value);
29627         }
29628     },
29629
29630     // private
29631     selectToday : function(){
29632         this.setValue(new Date().clearTime());
29633         this.fireEvent("select", this, this.value);
29634     },
29635
29636     // private
29637     update : function(date)
29638     {
29639         var vd = this.activeDate;
29640         this.activeDate = date;
29641         if(vd && this.el){
29642             var t = date.getTime();
29643             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29644                 this.cells.removeClass("x-date-selected");
29645                 this.cells.each(function(c){
29646                    if(c.dom.firstChild.dateValue == t){
29647                        c.addClass("x-date-selected");
29648                        setTimeout(function(){
29649                             try{c.dom.firstChild.focus();}catch(e){}
29650                        }, 50);
29651                        return false;
29652                    }
29653                 });
29654                 return;
29655             }
29656         }
29657         
29658         var days = date.getDaysInMonth();
29659         var firstOfMonth = date.getFirstDateOfMonth();
29660         var startingPos = firstOfMonth.getDay()-this.startDay;
29661
29662         if(startingPos <= this.startDay){
29663             startingPos += 7;
29664         }
29665
29666         var pm = date.add("mo", -1);
29667         var prevStart = pm.getDaysInMonth()-startingPos;
29668
29669         var cells = this.cells.elements;
29670         var textEls = this.textNodes;
29671         days += startingPos;
29672
29673         // convert everything to numbers so it's fast
29674         var day = 86400000;
29675         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29676         var today = new Date().clearTime().getTime();
29677         var sel = date.clearTime().getTime();
29678         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29679         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29680         var ddMatch = this.disabledDatesRE;
29681         var ddText = this.disabledDatesText;
29682         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29683         var ddaysText = this.disabledDaysText;
29684         var format = this.format;
29685
29686         var setCellClass = function(cal, cell){
29687             cell.title = "";
29688             var t = d.getTime();
29689             cell.firstChild.dateValue = t;
29690             if(t == today){
29691                 cell.className += " x-date-today";
29692                 cell.title = cal.todayText;
29693             }
29694             if(t == sel){
29695                 cell.className += " x-date-selected";
29696                 setTimeout(function(){
29697                     try{cell.firstChild.focus();}catch(e){}
29698                 }, 50);
29699             }
29700             // disabling
29701             if(t < min) {
29702                 cell.className = " x-date-disabled";
29703                 cell.title = cal.minText;
29704                 return;
29705             }
29706             if(t > max) {
29707                 cell.className = " x-date-disabled";
29708                 cell.title = cal.maxText;
29709                 return;
29710             }
29711             if(ddays){
29712                 if(ddays.indexOf(d.getDay()) != -1){
29713                     cell.title = ddaysText;
29714                     cell.className = " x-date-disabled";
29715                 }
29716             }
29717             if(ddMatch && format){
29718                 var fvalue = d.dateFormat(format);
29719                 if(ddMatch.test(fvalue)){
29720                     cell.title = ddText.replace("%0", fvalue);
29721                     cell.className = " x-date-disabled";
29722                 }
29723             }
29724         };
29725
29726         var i = 0;
29727         for(; i < startingPos; i++) {
29728             textEls[i].innerHTML = (++prevStart);
29729             d.setDate(d.getDate()+1);
29730             cells[i].className = "x-date-prevday";
29731             setCellClass(this, cells[i]);
29732         }
29733         for(; i < days; i++){
29734             intDay = i - startingPos + 1;
29735             textEls[i].innerHTML = (intDay);
29736             d.setDate(d.getDate()+1);
29737             cells[i].className = "x-date-active";
29738             setCellClass(this, cells[i]);
29739         }
29740         var extraDays = 0;
29741         for(; i < 42; i++) {
29742              textEls[i].innerHTML = (++extraDays);
29743              d.setDate(d.getDate()+1);
29744              cells[i].className = "x-date-nextday";
29745              setCellClass(this, cells[i]);
29746         }
29747
29748         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29749         this.fireEvent('monthchange', this, date);
29750         
29751         if(!this.internalRender){
29752             var main = this.el.dom.firstChild;
29753             var w = main.offsetWidth;
29754             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29755             Roo.fly(main).setWidth(w);
29756             this.internalRender = true;
29757             // opera does not respect the auto grow header center column
29758             // then, after it gets a width opera refuses to recalculate
29759             // without a second pass
29760             if(Roo.isOpera && !this.secondPass){
29761                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29762                 this.secondPass = true;
29763                 this.update.defer(10, this, [date]);
29764             }
29765         }
29766         
29767         
29768     }
29769 });        /*
29770  * Based on:
29771  * Ext JS Library 1.1.1
29772  * Copyright(c) 2006-2007, Ext JS, LLC.
29773  *
29774  * Originally Released Under LGPL - original licence link has changed is not relivant.
29775  *
29776  * Fork - LGPL
29777  * <script type="text/javascript">
29778  */
29779 /**
29780  * @class Roo.TabPanel
29781  * @extends Roo.util.Observable
29782  * A lightweight tab container.
29783  * <br><br>
29784  * Usage:
29785  * <pre><code>
29786 // basic tabs 1, built from existing content
29787 var tabs = new Roo.TabPanel("tabs1");
29788 tabs.addTab("script", "View Script");
29789 tabs.addTab("markup", "View Markup");
29790 tabs.activate("script");
29791
29792 // more advanced tabs, built from javascript
29793 var jtabs = new Roo.TabPanel("jtabs");
29794 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29795
29796 // set up the UpdateManager
29797 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29798 var updater = tab2.getUpdateManager();
29799 updater.setDefaultUrl("ajax1.htm");
29800 tab2.on('activate', updater.refresh, updater, true);
29801
29802 // Use setUrl for Ajax loading
29803 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29804 tab3.setUrl("ajax2.htm", null, true);
29805
29806 // Disabled tab
29807 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29808 tab4.disable();
29809
29810 jtabs.activate("jtabs-1");
29811  * </code></pre>
29812  * @constructor
29813  * Create a new TabPanel.
29814  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29815  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29816  */
29817 Roo.TabPanel = function(container, config){
29818     /**
29819     * The container element for this TabPanel.
29820     * @type Roo.Element
29821     */
29822     this.el = Roo.get(container, true);
29823     if(config){
29824         if(typeof config == "boolean"){
29825             this.tabPosition = config ? "bottom" : "top";
29826         }else{
29827             Roo.apply(this, config);
29828         }
29829     }
29830     if(this.tabPosition == "bottom"){
29831         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29832         this.el.addClass("x-tabs-bottom");
29833     }
29834     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29835     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29836     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29837     if(Roo.isIE){
29838         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29839     }
29840     if(this.tabPosition != "bottom"){
29841         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29842          * @type Roo.Element
29843          */
29844         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29845         this.el.addClass("x-tabs-top");
29846     }
29847     this.items = [];
29848
29849     this.bodyEl.setStyle("position", "relative");
29850
29851     this.active = null;
29852     this.activateDelegate = this.activate.createDelegate(this);
29853
29854     this.addEvents({
29855         /**
29856          * @event tabchange
29857          * Fires when the active tab changes
29858          * @param {Roo.TabPanel} this
29859          * @param {Roo.TabPanelItem} activePanel The new active tab
29860          */
29861         "tabchange": true,
29862         /**
29863          * @event beforetabchange
29864          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29865          * @param {Roo.TabPanel} this
29866          * @param {Object} e Set cancel to true on this object to cancel the tab change
29867          * @param {Roo.TabPanelItem} tab The tab being changed to
29868          */
29869         "beforetabchange" : true
29870     });
29871
29872     Roo.EventManager.onWindowResize(this.onResize, this);
29873     this.cpad = this.el.getPadding("lr");
29874     this.hiddenCount = 0;
29875
29876
29877     // toolbar on the tabbar support...
29878     if (this.toolbar) {
29879         var tcfg = this.toolbar;
29880         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29881         this.toolbar = new Roo.Toolbar(tcfg);
29882         if (Roo.isSafari) {
29883             var tbl = tcfg.container.child('table', true);
29884             tbl.setAttribute('width', '100%');
29885         }
29886         
29887     }
29888    
29889
29890
29891     Roo.TabPanel.superclass.constructor.call(this);
29892 };
29893
29894 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29895     /*
29896      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29897      */
29898     tabPosition : "top",
29899     /*
29900      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29901      */
29902     currentTabWidth : 0,
29903     /*
29904      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29905      */
29906     minTabWidth : 40,
29907     /*
29908      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29909      */
29910     maxTabWidth : 250,
29911     /*
29912      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29913      */
29914     preferredTabWidth : 175,
29915     /*
29916      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29917      */
29918     resizeTabs : false,
29919     /*
29920      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29921      */
29922     monitorResize : true,
29923     /*
29924      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29925      */
29926     toolbar : false,
29927
29928     /**
29929      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29930      * @param {String} id The id of the div to use <b>or create</b>
29931      * @param {String} text The text for the tab
29932      * @param {String} content (optional) Content to put in the TabPanelItem body
29933      * @param {Boolean} closable (optional) True to create a close icon on the tab
29934      * @return {Roo.TabPanelItem} The created TabPanelItem
29935      */
29936     addTab : function(id, text, content, closable){
29937         var item = new Roo.TabPanelItem(this, id, text, closable);
29938         this.addTabItem(item);
29939         if(content){
29940             item.setContent(content);
29941         }
29942         return item;
29943     },
29944
29945     /**
29946      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29947      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29948      * @return {Roo.TabPanelItem}
29949      */
29950     getTab : function(id){
29951         return this.items[id];
29952     },
29953
29954     /**
29955      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29956      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29957      */
29958     hideTab : function(id){
29959         var t = this.items[id];
29960         if(!t.isHidden()){
29961            t.setHidden(true);
29962            this.hiddenCount++;
29963            this.autoSizeTabs();
29964         }
29965     },
29966
29967     /**
29968      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29969      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29970      */
29971     unhideTab : function(id){
29972         var t = this.items[id];
29973         if(t.isHidden()){
29974            t.setHidden(false);
29975            this.hiddenCount--;
29976            this.autoSizeTabs();
29977         }
29978     },
29979
29980     /**
29981      * Adds an existing {@link Roo.TabPanelItem}.
29982      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29983      */
29984     addTabItem : function(item){
29985         this.items[item.id] = item;
29986         this.items.push(item);
29987         if(this.resizeTabs){
29988            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29989            this.autoSizeTabs();
29990         }else{
29991             item.autoSize();
29992         }
29993     },
29994
29995     /**
29996      * Removes a {@link Roo.TabPanelItem}.
29997      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29998      */
29999     removeTab : function(id){
30000         var items = this.items;
30001         var tab = items[id];
30002         if(!tab) { return; }
30003         var index = items.indexOf(tab);
30004         if(this.active == tab && items.length > 1){
30005             var newTab = this.getNextAvailable(index);
30006             if(newTab) {
30007                 newTab.activate();
30008             }
30009         }
30010         this.stripEl.dom.removeChild(tab.pnode.dom);
30011         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
30012             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
30013         }
30014         items.splice(index, 1);
30015         delete this.items[tab.id];
30016         tab.fireEvent("close", tab);
30017         tab.purgeListeners();
30018         this.autoSizeTabs();
30019     },
30020
30021     getNextAvailable : function(start){
30022         var items = this.items;
30023         var index = start;
30024         // look for a next tab that will slide over to
30025         // replace the one being removed
30026         while(index < items.length){
30027             var item = items[++index];
30028             if(item && !item.isHidden()){
30029                 return item;
30030             }
30031         }
30032         // if one isn't found select the previous tab (on the left)
30033         index = start;
30034         while(index >= 0){
30035             var item = items[--index];
30036             if(item && !item.isHidden()){
30037                 return item;
30038             }
30039         }
30040         return null;
30041     },
30042
30043     /**
30044      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30045      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30046      */
30047     disableTab : function(id){
30048         var tab = this.items[id];
30049         if(tab && this.active != tab){
30050             tab.disable();
30051         }
30052     },
30053
30054     /**
30055      * Enables a {@link Roo.TabPanelItem} that is disabled.
30056      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30057      */
30058     enableTab : function(id){
30059         var tab = this.items[id];
30060         tab.enable();
30061     },
30062
30063     /**
30064      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30065      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30066      * @return {Roo.TabPanelItem} The TabPanelItem.
30067      */
30068     activate : function(id){
30069         var tab = this.items[id];
30070         if(!tab){
30071             return null;
30072         }
30073         if(tab == this.active || tab.disabled){
30074             return tab;
30075         }
30076         var e = {};
30077         this.fireEvent("beforetabchange", this, e, tab);
30078         if(e.cancel !== true && !tab.disabled){
30079             if(this.active){
30080                 this.active.hide();
30081             }
30082             this.active = this.items[id];
30083             this.active.show();
30084             this.fireEvent("tabchange", this, this.active);
30085         }
30086         return tab;
30087     },
30088
30089     /**
30090      * Gets the active {@link Roo.TabPanelItem}.
30091      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30092      */
30093     getActiveTab : function(){
30094         return this.active;
30095     },
30096
30097     /**
30098      * Updates the tab body element to fit the height of the container element
30099      * for overflow scrolling
30100      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30101      */
30102     syncHeight : function(targetHeight){
30103         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30104         var bm = this.bodyEl.getMargins();
30105         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30106         this.bodyEl.setHeight(newHeight);
30107         return newHeight;
30108     },
30109
30110     onResize : function(){
30111         if(this.monitorResize){
30112             this.autoSizeTabs();
30113         }
30114     },
30115
30116     /**
30117      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30118      */
30119     beginUpdate : function(){
30120         this.updating = true;
30121     },
30122
30123     /**
30124      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30125      */
30126     endUpdate : function(){
30127         this.updating = false;
30128         this.autoSizeTabs();
30129     },
30130
30131     /**
30132      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30133      */
30134     autoSizeTabs : function(){
30135         var count = this.items.length;
30136         var vcount = count - this.hiddenCount;
30137         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30138             return;
30139         }
30140         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30141         var availWidth = Math.floor(w / vcount);
30142         var b = this.stripBody;
30143         if(b.getWidth() > w){
30144             var tabs = this.items;
30145             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30146             if(availWidth < this.minTabWidth){
30147                 /*if(!this.sleft){    // incomplete scrolling code
30148                     this.createScrollButtons();
30149                 }
30150                 this.showScroll();
30151                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30152             }
30153         }else{
30154             if(this.currentTabWidth < this.preferredTabWidth){
30155                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30156             }
30157         }
30158     },
30159
30160     /**
30161      * Returns the number of tabs in this TabPanel.
30162      * @return {Number}
30163      */
30164      getCount : function(){
30165          return this.items.length;
30166      },
30167
30168     /**
30169      * Resizes all the tabs to the passed width
30170      * @param {Number} The new width
30171      */
30172     setTabWidth : function(width){
30173         this.currentTabWidth = width;
30174         for(var i = 0, len = this.items.length; i < len; i++) {
30175                 if(!this.items[i].isHidden()) {
30176                 this.items[i].setWidth(width);
30177             }
30178         }
30179     },
30180
30181     /**
30182      * Destroys this TabPanel
30183      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30184      */
30185     destroy : function(removeEl){
30186         Roo.EventManager.removeResizeListener(this.onResize, this);
30187         for(var i = 0, len = this.items.length; i < len; i++){
30188             this.items[i].purgeListeners();
30189         }
30190         if(removeEl === true){
30191             this.el.update("");
30192             this.el.remove();
30193         }
30194     }
30195 });
30196
30197 /**
30198  * @class Roo.TabPanelItem
30199  * @extends Roo.util.Observable
30200  * Represents an individual item (tab plus body) in a TabPanel.
30201  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30202  * @param {String} id The id of this TabPanelItem
30203  * @param {String} text The text for the tab of this TabPanelItem
30204  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30205  */
30206 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30207     /**
30208      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30209      * @type Roo.TabPanel
30210      */
30211     this.tabPanel = tabPanel;
30212     /**
30213      * The id for this TabPanelItem
30214      * @type String
30215      */
30216     this.id = id;
30217     /** @private */
30218     this.disabled = false;
30219     /** @private */
30220     this.text = text;
30221     /** @private */
30222     this.loaded = false;
30223     this.closable = closable;
30224
30225     /**
30226      * The body element for this TabPanelItem.
30227      * @type Roo.Element
30228      */
30229     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30230     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30231     this.bodyEl.setStyle("display", "block");
30232     this.bodyEl.setStyle("zoom", "1");
30233     this.hideAction();
30234
30235     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30236     /** @private */
30237     this.el = Roo.get(els.el, true);
30238     this.inner = Roo.get(els.inner, true);
30239     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30240     this.pnode = Roo.get(els.el.parentNode, true);
30241     this.el.on("mousedown", this.onTabMouseDown, this);
30242     this.el.on("click", this.onTabClick, this);
30243     /** @private */
30244     if(closable){
30245         var c = Roo.get(els.close, true);
30246         c.dom.title = this.closeText;
30247         c.addClassOnOver("close-over");
30248         c.on("click", this.closeClick, this);
30249      }
30250
30251     this.addEvents({
30252          /**
30253          * @event activate
30254          * Fires when this tab becomes the active tab.
30255          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30256          * @param {Roo.TabPanelItem} this
30257          */
30258         "activate": true,
30259         /**
30260          * @event beforeclose
30261          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30262          * @param {Roo.TabPanelItem} this
30263          * @param {Object} e Set cancel to true on this object to cancel the close.
30264          */
30265         "beforeclose": true,
30266         /**
30267          * @event close
30268          * Fires when this tab is closed.
30269          * @param {Roo.TabPanelItem} this
30270          */
30271          "close": true,
30272         /**
30273          * @event deactivate
30274          * Fires when this tab is no longer the active tab.
30275          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30276          * @param {Roo.TabPanelItem} this
30277          */
30278          "deactivate" : true
30279     });
30280     this.hidden = false;
30281
30282     Roo.TabPanelItem.superclass.constructor.call(this);
30283 };
30284
30285 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30286     purgeListeners : function(){
30287        Roo.util.Observable.prototype.purgeListeners.call(this);
30288        this.el.removeAllListeners();
30289     },
30290     /**
30291      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30292      */
30293     show : function(){
30294         this.pnode.addClass("on");
30295         this.showAction();
30296         if(Roo.isOpera){
30297             this.tabPanel.stripWrap.repaint();
30298         }
30299         this.fireEvent("activate", this.tabPanel, this);
30300     },
30301
30302     /**
30303      * Returns true if this tab is the active tab.
30304      * @return {Boolean}
30305      */
30306     isActive : function(){
30307         return this.tabPanel.getActiveTab() == this;
30308     },
30309
30310     /**
30311      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30312      */
30313     hide : function(){
30314         this.pnode.removeClass("on");
30315         this.hideAction();
30316         this.fireEvent("deactivate", this.tabPanel, this);
30317     },
30318
30319     hideAction : function(){
30320         this.bodyEl.hide();
30321         this.bodyEl.setStyle("position", "absolute");
30322         this.bodyEl.setLeft("-20000px");
30323         this.bodyEl.setTop("-20000px");
30324     },
30325
30326     showAction : function(){
30327         this.bodyEl.setStyle("position", "relative");
30328         this.bodyEl.setTop("");
30329         this.bodyEl.setLeft("");
30330         this.bodyEl.show();
30331     },
30332
30333     /**
30334      * Set the tooltip for the tab.
30335      * @param {String} tooltip The tab's tooltip
30336      */
30337     setTooltip : function(text){
30338         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30339             this.textEl.dom.qtip = text;
30340             this.textEl.dom.removeAttribute('title');
30341         }else{
30342             this.textEl.dom.title = text;
30343         }
30344     },
30345
30346     onTabClick : function(e){
30347         e.preventDefault();
30348         this.tabPanel.activate(this.id);
30349     },
30350
30351     onTabMouseDown : function(e){
30352         e.preventDefault();
30353         this.tabPanel.activate(this.id);
30354     },
30355
30356     getWidth : function(){
30357         return this.inner.getWidth();
30358     },
30359
30360     setWidth : function(width){
30361         var iwidth = width - this.pnode.getPadding("lr");
30362         this.inner.setWidth(iwidth);
30363         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30364         this.pnode.setWidth(width);
30365     },
30366
30367     /**
30368      * Show or hide the tab
30369      * @param {Boolean} hidden True to hide or false to show.
30370      */
30371     setHidden : function(hidden){
30372         this.hidden = hidden;
30373         this.pnode.setStyle("display", hidden ? "none" : "");
30374     },
30375
30376     /**
30377      * Returns true if this tab is "hidden"
30378      * @return {Boolean}
30379      */
30380     isHidden : function(){
30381         return this.hidden;
30382     },
30383
30384     /**
30385      * Returns the text for this tab
30386      * @return {String}
30387      */
30388     getText : function(){
30389         return this.text;
30390     },
30391
30392     autoSize : function(){
30393         //this.el.beginMeasure();
30394         this.textEl.setWidth(1);
30395         /*
30396          *  #2804 [new] Tabs in Roojs
30397          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30398          */
30399         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30400         //this.el.endMeasure();
30401     },
30402
30403     /**
30404      * Sets the text for the tab (Note: this also sets the tooltip text)
30405      * @param {String} text The tab's text and tooltip
30406      */
30407     setText : function(text){
30408         this.text = text;
30409         this.textEl.update(text);
30410         this.setTooltip(text);
30411         if(!this.tabPanel.resizeTabs){
30412             this.autoSize();
30413         }
30414     },
30415     /**
30416      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30417      */
30418     activate : function(){
30419         this.tabPanel.activate(this.id);
30420     },
30421
30422     /**
30423      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30424      */
30425     disable : function(){
30426         if(this.tabPanel.active != this){
30427             this.disabled = true;
30428             this.pnode.addClass("disabled");
30429         }
30430     },
30431
30432     /**
30433      * Enables this TabPanelItem if it was previously disabled.
30434      */
30435     enable : function(){
30436         this.disabled = false;
30437         this.pnode.removeClass("disabled");
30438     },
30439
30440     /**
30441      * Sets the content for this TabPanelItem.
30442      * @param {String} content The content
30443      * @param {Boolean} loadScripts true to look for and load scripts
30444      */
30445     setContent : function(content, loadScripts){
30446         this.bodyEl.update(content, loadScripts);
30447     },
30448
30449     /**
30450      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30451      * @return {Roo.UpdateManager} The UpdateManager
30452      */
30453     getUpdateManager : function(){
30454         return this.bodyEl.getUpdateManager();
30455     },
30456
30457     /**
30458      * Set a URL to be used to load the content for this TabPanelItem.
30459      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30460      * @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)
30461      * @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)
30462      * @return {Roo.UpdateManager} The UpdateManager
30463      */
30464     setUrl : function(url, params, loadOnce){
30465         if(this.refreshDelegate){
30466             this.un('activate', this.refreshDelegate);
30467         }
30468         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30469         this.on("activate", this.refreshDelegate);
30470         return this.bodyEl.getUpdateManager();
30471     },
30472
30473     /** @private */
30474     _handleRefresh : function(url, params, loadOnce){
30475         if(!loadOnce || !this.loaded){
30476             var updater = this.bodyEl.getUpdateManager();
30477             updater.update(url, params, this._setLoaded.createDelegate(this));
30478         }
30479     },
30480
30481     /**
30482      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30483      *   Will fail silently if the setUrl method has not been called.
30484      *   This does not activate the panel, just updates its content.
30485      */
30486     refresh : function(){
30487         if(this.refreshDelegate){
30488            this.loaded = false;
30489            this.refreshDelegate();
30490         }
30491     },
30492
30493     /** @private */
30494     _setLoaded : function(){
30495         this.loaded = true;
30496     },
30497
30498     /** @private */
30499     closeClick : function(e){
30500         var o = {};
30501         e.stopEvent();
30502         this.fireEvent("beforeclose", this, o);
30503         if(o.cancel !== true){
30504             this.tabPanel.removeTab(this.id);
30505         }
30506     },
30507     /**
30508      * The text displayed in the tooltip for the close icon.
30509      * @type String
30510      */
30511     closeText : "Close this tab"
30512 });
30513
30514 /** @private */
30515 Roo.TabPanel.prototype.createStrip = function(container){
30516     var strip = document.createElement("div");
30517     strip.className = "x-tabs-wrap";
30518     container.appendChild(strip);
30519     return strip;
30520 };
30521 /** @private */
30522 Roo.TabPanel.prototype.createStripList = function(strip){
30523     // div wrapper for retard IE
30524     // returns the "tr" element.
30525     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30526         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30527         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30528     return strip.firstChild.firstChild.firstChild.firstChild;
30529 };
30530 /** @private */
30531 Roo.TabPanel.prototype.createBody = function(container){
30532     var body = document.createElement("div");
30533     Roo.id(body, "tab-body");
30534     Roo.fly(body).addClass("x-tabs-body");
30535     container.appendChild(body);
30536     return body;
30537 };
30538 /** @private */
30539 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30540     var body = Roo.getDom(id);
30541     if(!body){
30542         body = document.createElement("div");
30543         body.id = id;
30544     }
30545     Roo.fly(body).addClass("x-tabs-item-body");
30546     bodyEl.insertBefore(body, bodyEl.firstChild);
30547     return body;
30548 };
30549 /** @private */
30550 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30551     var td = document.createElement("td");
30552     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30553     //stripEl.appendChild(td);
30554     if(closable){
30555         td.className = "x-tabs-closable";
30556         if(!this.closeTpl){
30557             this.closeTpl = new Roo.Template(
30558                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30559                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30560                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30561             );
30562         }
30563         var el = this.closeTpl.overwrite(td, {"text": text});
30564         var close = el.getElementsByTagName("div")[0];
30565         var inner = el.getElementsByTagName("em")[0];
30566         return {"el": el, "close": close, "inner": inner};
30567     } else {
30568         if(!this.tabTpl){
30569             this.tabTpl = new Roo.Template(
30570                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30571                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30572             );
30573         }
30574         var el = this.tabTpl.overwrite(td, {"text": text});
30575         var inner = el.getElementsByTagName("em")[0];
30576         return {"el": el, "inner": inner};
30577     }
30578 };/*
30579  * Based on:
30580  * Ext JS Library 1.1.1
30581  * Copyright(c) 2006-2007, Ext JS, LLC.
30582  *
30583  * Originally Released Under LGPL - original licence link has changed is not relivant.
30584  *
30585  * Fork - LGPL
30586  * <script type="text/javascript">
30587  */
30588
30589 /**
30590  * @class Roo.Button
30591  * @extends Roo.util.Observable
30592  * Simple Button class
30593  * @cfg {String} text The button text
30594  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30595  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30596  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30597  * @cfg {Object} scope The scope of the handler
30598  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30599  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30600  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30601  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30602  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30603  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30604    applies if enableToggle = true)
30605  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30606  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30607   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30608  * @constructor
30609  * Create a new button
30610  * @param {Object} config The config object
30611  */
30612 Roo.Button = function(renderTo, config)
30613 {
30614     if (!config) {
30615         config = renderTo;
30616         renderTo = config.renderTo || false;
30617     }
30618     
30619     Roo.apply(this, config);
30620     this.addEvents({
30621         /**
30622              * @event click
30623              * Fires when this button is clicked
30624              * @param {Button} this
30625              * @param {EventObject} e The click event
30626              */
30627             "click" : true,
30628         /**
30629              * @event toggle
30630              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30631              * @param {Button} this
30632              * @param {Boolean} pressed
30633              */
30634             "toggle" : true,
30635         /**
30636              * @event mouseover
30637              * Fires when the mouse hovers over the button
30638              * @param {Button} this
30639              * @param {Event} e The event object
30640              */
30641         'mouseover' : true,
30642         /**
30643              * @event mouseout
30644              * Fires when the mouse exits the button
30645              * @param {Button} this
30646              * @param {Event} e The event object
30647              */
30648         'mouseout': true,
30649          /**
30650              * @event render
30651              * Fires when the button is rendered
30652              * @param {Button} this
30653              */
30654         'render': true
30655     });
30656     if(this.menu){
30657         this.menu = Roo.menu.MenuMgr.get(this.menu);
30658     }
30659     // register listeners first!!  - so render can be captured..
30660     Roo.util.Observable.call(this);
30661     if(renderTo){
30662         this.render(renderTo);
30663     }
30664     
30665   
30666 };
30667
30668 Roo.extend(Roo.Button, Roo.util.Observable, {
30669     /**
30670      * 
30671      */
30672     
30673     /**
30674      * Read-only. True if this button is hidden
30675      * @type Boolean
30676      */
30677     hidden : false,
30678     /**
30679      * Read-only. True if this button is disabled
30680      * @type Boolean
30681      */
30682     disabled : false,
30683     /**
30684      * Read-only. True if this button is pressed (only if enableToggle = true)
30685      * @type Boolean
30686      */
30687     pressed : false,
30688
30689     /**
30690      * @cfg {Number} tabIndex 
30691      * The DOM tabIndex for this button (defaults to undefined)
30692      */
30693     tabIndex : undefined,
30694
30695     /**
30696      * @cfg {Boolean} enableToggle
30697      * True to enable pressed/not pressed toggling (defaults to false)
30698      */
30699     enableToggle: false,
30700     /**
30701      * @cfg {Roo.menu.Menu} menu
30702      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30703      */
30704     menu : undefined,
30705     /**
30706      * @cfg {String} menuAlign
30707      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30708      */
30709     menuAlign : "tl-bl?",
30710
30711     /**
30712      * @cfg {String} iconCls
30713      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30714      */
30715     iconCls : undefined,
30716     /**
30717      * @cfg {String} type
30718      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30719      */
30720     type : 'button',
30721
30722     // private
30723     menuClassTarget: 'tr',
30724
30725     /**
30726      * @cfg {String} clickEvent
30727      * The type of event to map to the button's event handler (defaults to 'click')
30728      */
30729     clickEvent : 'click',
30730
30731     /**
30732      * @cfg {Boolean} handleMouseEvents
30733      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30734      */
30735     handleMouseEvents : true,
30736
30737     /**
30738      * @cfg {String} tooltipType
30739      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30740      */
30741     tooltipType : 'qtip',
30742
30743     /**
30744      * @cfg {String} cls
30745      * A CSS class to apply to the button's main element.
30746      */
30747     
30748     /**
30749      * @cfg {Roo.Template} template (Optional)
30750      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30751      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30752      * require code modifications if required elements (e.g. a button) aren't present.
30753      */
30754
30755     // private
30756     render : function(renderTo){
30757         var btn;
30758         if(this.hideParent){
30759             this.parentEl = Roo.get(renderTo);
30760         }
30761         if(!this.dhconfig){
30762             if(!this.template){
30763                 if(!Roo.Button.buttonTemplate){
30764                     // hideous table template
30765                     Roo.Button.buttonTemplate = new Roo.Template(
30766                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30767                         '<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>',
30768                         "</tr></tbody></table>");
30769                 }
30770                 this.template = Roo.Button.buttonTemplate;
30771             }
30772             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30773             var btnEl = btn.child("button:first");
30774             btnEl.on('focus', this.onFocus, this);
30775             btnEl.on('blur', this.onBlur, this);
30776             if(this.cls){
30777                 btn.addClass(this.cls);
30778             }
30779             if(this.icon){
30780                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30781             }
30782             if(this.iconCls){
30783                 btnEl.addClass(this.iconCls);
30784                 if(!this.cls){
30785                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30786                 }
30787             }
30788             if(this.tabIndex !== undefined){
30789                 btnEl.dom.tabIndex = this.tabIndex;
30790             }
30791             if(this.tooltip){
30792                 if(typeof this.tooltip == 'object'){
30793                     Roo.QuickTips.tips(Roo.apply({
30794                           target: btnEl.id
30795                     }, this.tooltip));
30796                 } else {
30797                     btnEl.dom[this.tooltipType] = this.tooltip;
30798                 }
30799             }
30800         }else{
30801             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30802         }
30803         this.el = btn;
30804         if(this.id){
30805             this.el.dom.id = this.el.id = this.id;
30806         }
30807         if(this.menu){
30808             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30809             this.menu.on("show", this.onMenuShow, this);
30810             this.menu.on("hide", this.onMenuHide, this);
30811         }
30812         btn.addClass("x-btn");
30813         if(Roo.isIE && !Roo.isIE7){
30814             this.autoWidth.defer(1, this);
30815         }else{
30816             this.autoWidth();
30817         }
30818         if(this.handleMouseEvents){
30819             btn.on("mouseover", this.onMouseOver, this);
30820             btn.on("mouseout", this.onMouseOut, this);
30821             btn.on("mousedown", this.onMouseDown, this);
30822         }
30823         btn.on(this.clickEvent, this.onClick, this);
30824         //btn.on("mouseup", this.onMouseUp, this);
30825         if(this.hidden){
30826             this.hide();
30827         }
30828         if(this.disabled){
30829             this.disable();
30830         }
30831         Roo.ButtonToggleMgr.register(this);
30832         if(this.pressed){
30833             this.el.addClass("x-btn-pressed");
30834         }
30835         if(this.repeat){
30836             var repeater = new Roo.util.ClickRepeater(btn,
30837                 typeof this.repeat == "object" ? this.repeat : {}
30838             );
30839             repeater.on("click", this.onClick,  this);
30840         }
30841         
30842         this.fireEvent('render', this);
30843         
30844     },
30845     /**
30846      * Returns the button's underlying element
30847      * @return {Roo.Element} The element
30848      */
30849     getEl : function(){
30850         return this.el;  
30851     },
30852     
30853     /**
30854      * Destroys this Button and removes any listeners.
30855      */
30856     destroy : function(){
30857         Roo.ButtonToggleMgr.unregister(this);
30858         this.el.removeAllListeners();
30859         this.purgeListeners();
30860         this.el.remove();
30861     },
30862
30863     // private
30864     autoWidth : function(){
30865         if(this.el){
30866             this.el.setWidth("auto");
30867             if(Roo.isIE7 && Roo.isStrict){
30868                 var ib = this.el.child('button');
30869                 if(ib && ib.getWidth() > 20){
30870                     ib.clip();
30871                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30872                 }
30873             }
30874             if(this.minWidth){
30875                 if(this.hidden){
30876                     this.el.beginMeasure();
30877                 }
30878                 if(this.el.getWidth() < this.minWidth){
30879                     this.el.setWidth(this.minWidth);
30880                 }
30881                 if(this.hidden){
30882                     this.el.endMeasure();
30883                 }
30884             }
30885         }
30886     },
30887
30888     /**
30889      * Assigns this button's click handler
30890      * @param {Function} handler The function to call when the button is clicked
30891      * @param {Object} scope (optional) Scope for the function passed in
30892      */
30893     setHandler : function(handler, scope){
30894         this.handler = handler;
30895         this.scope = scope;  
30896     },
30897     
30898     /**
30899      * Sets this button's text
30900      * @param {String} text The button text
30901      */
30902     setText : function(text){
30903         this.text = text;
30904         if(this.el){
30905             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30906         }
30907         this.autoWidth();
30908     },
30909     
30910     /**
30911      * Gets the text for this button
30912      * @return {String} The button text
30913      */
30914     getText : function(){
30915         return this.text;  
30916     },
30917     
30918     /**
30919      * Show this button
30920      */
30921     show: function(){
30922         this.hidden = false;
30923         if(this.el){
30924             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30925         }
30926     },
30927     
30928     /**
30929      * Hide this button
30930      */
30931     hide: function(){
30932         this.hidden = true;
30933         if(this.el){
30934             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30935         }
30936     },
30937     
30938     /**
30939      * Convenience function for boolean show/hide
30940      * @param {Boolean} visible True to show, false to hide
30941      */
30942     setVisible: function(visible){
30943         if(visible) {
30944             this.show();
30945         }else{
30946             this.hide();
30947         }
30948     },
30949     
30950     /**
30951      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30952      * @param {Boolean} state (optional) Force a particular state
30953      */
30954     toggle : function(state){
30955         state = state === undefined ? !this.pressed : state;
30956         if(state != this.pressed){
30957             if(state){
30958                 this.el.addClass("x-btn-pressed");
30959                 this.pressed = true;
30960                 this.fireEvent("toggle", this, true);
30961             }else{
30962                 this.el.removeClass("x-btn-pressed");
30963                 this.pressed = false;
30964                 this.fireEvent("toggle", this, false);
30965             }
30966             if(this.toggleHandler){
30967                 this.toggleHandler.call(this.scope || this, this, state);
30968             }
30969         }
30970     },
30971     
30972     /**
30973      * Focus the button
30974      */
30975     focus : function(){
30976         this.el.child('button:first').focus();
30977     },
30978     
30979     /**
30980      * Disable this button
30981      */
30982     disable : function(){
30983         if(this.el){
30984             this.el.addClass("x-btn-disabled");
30985         }
30986         this.disabled = true;
30987     },
30988     
30989     /**
30990      * Enable this button
30991      */
30992     enable : function(){
30993         if(this.el){
30994             this.el.removeClass("x-btn-disabled");
30995         }
30996         this.disabled = false;
30997     },
30998
30999     /**
31000      * Convenience function for boolean enable/disable
31001      * @param {Boolean} enabled True to enable, false to disable
31002      */
31003     setDisabled : function(v){
31004         this[v !== true ? "enable" : "disable"]();
31005     },
31006
31007     // private
31008     onClick : function(e)
31009     {
31010         if(e){
31011             e.preventDefault();
31012         }
31013         if(e.button != 0){
31014             return;
31015         }
31016         if(!this.disabled){
31017             if(this.enableToggle){
31018                 this.toggle();
31019             }
31020             if(this.menu && !this.menu.isVisible()){
31021                 this.menu.show(this.el, this.menuAlign);
31022             }
31023             this.fireEvent("click", this, e);
31024             if(this.handler){
31025                 this.el.removeClass("x-btn-over");
31026                 this.handler.call(this.scope || this, this, e);
31027             }
31028         }
31029     },
31030     // private
31031     onMouseOver : function(e){
31032         if(!this.disabled){
31033             this.el.addClass("x-btn-over");
31034             this.fireEvent('mouseover', this, e);
31035         }
31036     },
31037     // private
31038     onMouseOut : function(e){
31039         if(!e.within(this.el,  true)){
31040             this.el.removeClass("x-btn-over");
31041             this.fireEvent('mouseout', this, e);
31042         }
31043     },
31044     // private
31045     onFocus : function(e){
31046         if(!this.disabled){
31047             this.el.addClass("x-btn-focus");
31048         }
31049     },
31050     // private
31051     onBlur : function(e){
31052         this.el.removeClass("x-btn-focus");
31053     },
31054     // private
31055     onMouseDown : function(e){
31056         if(!this.disabled && e.button == 0){
31057             this.el.addClass("x-btn-click");
31058             Roo.get(document).on('mouseup', this.onMouseUp, this);
31059         }
31060     },
31061     // private
31062     onMouseUp : function(e){
31063         if(e.button == 0){
31064             this.el.removeClass("x-btn-click");
31065             Roo.get(document).un('mouseup', this.onMouseUp, this);
31066         }
31067     },
31068     // private
31069     onMenuShow : function(e){
31070         this.el.addClass("x-btn-menu-active");
31071     },
31072     // private
31073     onMenuHide : function(e){
31074         this.el.removeClass("x-btn-menu-active");
31075     }   
31076 });
31077
31078 // Private utility class used by Button
31079 Roo.ButtonToggleMgr = function(){
31080    var groups = {};
31081    
31082    function toggleGroup(btn, state){
31083        if(state){
31084            var g = groups[btn.toggleGroup];
31085            for(var i = 0, l = g.length; i < l; i++){
31086                if(g[i] != btn){
31087                    g[i].toggle(false);
31088                }
31089            }
31090        }
31091    }
31092    
31093    return {
31094        register : function(btn){
31095            if(!btn.toggleGroup){
31096                return;
31097            }
31098            var g = groups[btn.toggleGroup];
31099            if(!g){
31100                g = groups[btn.toggleGroup] = [];
31101            }
31102            g.push(btn);
31103            btn.on("toggle", toggleGroup);
31104        },
31105        
31106        unregister : function(btn){
31107            if(!btn.toggleGroup){
31108                return;
31109            }
31110            var g = groups[btn.toggleGroup];
31111            if(g){
31112                g.remove(btn);
31113                btn.un("toggle", toggleGroup);
31114            }
31115        }
31116    };
31117 }();/*
31118  * Based on:
31119  * Ext JS Library 1.1.1
31120  * Copyright(c) 2006-2007, Ext JS, LLC.
31121  *
31122  * Originally Released Under LGPL - original licence link has changed is not relivant.
31123  *
31124  * Fork - LGPL
31125  * <script type="text/javascript">
31126  */
31127  
31128 /**
31129  * @class Roo.SplitButton
31130  * @extends Roo.Button
31131  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31132  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31133  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31134  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31135  * @cfg {String} arrowTooltip The title attribute of the arrow
31136  * @constructor
31137  * Create a new menu button
31138  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31139  * @param {Object} config The config object
31140  */
31141 Roo.SplitButton = function(renderTo, config){
31142     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31143     /**
31144      * @event arrowclick
31145      * Fires when this button's arrow is clicked
31146      * @param {SplitButton} this
31147      * @param {EventObject} e The click event
31148      */
31149     this.addEvents({"arrowclick":true});
31150 };
31151
31152 Roo.extend(Roo.SplitButton, Roo.Button, {
31153     render : function(renderTo){
31154         // this is one sweet looking template!
31155         var tpl = new Roo.Template(
31156             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31157             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31158             '<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>',
31159             "</tbody></table></td><td>",
31160             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31161             '<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>',
31162             "</tbody></table></td></tr></table>"
31163         );
31164         var btn = tpl.append(renderTo, [this.text, this.type], true);
31165         var btnEl = btn.child("button");
31166         if(this.cls){
31167             btn.addClass(this.cls);
31168         }
31169         if(this.icon){
31170             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31171         }
31172         if(this.iconCls){
31173             btnEl.addClass(this.iconCls);
31174             if(!this.cls){
31175                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31176             }
31177         }
31178         this.el = btn;
31179         if(this.handleMouseEvents){
31180             btn.on("mouseover", this.onMouseOver, this);
31181             btn.on("mouseout", this.onMouseOut, this);
31182             btn.on("mousedown", this.onMouseDown, this);
31183             btn.on("mouseup", this.onMouseUp, this);
31184         }
31185         btn.on(this.clickEvent, this.onClick, this);
31186         if(this.tooltip){
31187             if(typeof this.tooltip == 'object'){
31188                 Roo.QuickTips.tips(Roo.apply({
31189                       target: btnEl.id
31190                 }, this.tooltip));
31191             } else {
31192                 btnEl.dom[this.tooltipType] = this.tooltip;
31193             }
31194         }
31195         if(this.arrowTooltip){
31196             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31197         }
31198         if(this.hidden){
31199             this.hide();
31200         }
31201         if(this.disabled){
31202             this.disable();
31203         }
31204         if(this.pressed){
31205             this.el.addClass("x-btn-pressed");
31206         }
31207         if(Roo.isIE && !Roo.isIE7){
31208             this.autoWidth.defer(1, this);
31209         }else{
31210             this.autoWidth();
31211         }
31212         if(this.menu){
31213             this.menu.on("show", this.onMenuShow, this);
31214             this.menu.on("hide", this.onMenuHide, this);
31215         }
31216         this.fireEvent('render', this);
31217     },
31218
31219     // private
31220     autoWidth : function(){
31221         if(this.el){
31222             var tbl = this.el.child("table:first");
31223             var tbl2 = this.el.child("table:last");
31224             this.el.setWidth("auto");
31225             tbl.setWidth("auto");
31226             if(Roo.isIE7 && Roo.isStrict){
31227                 var ib = this.el.child('button:first');
31228                 if(ib && ib.getWidth() > 20){
31229                     ib.clip();
31230                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31231                 }
31232             }
31233             if(this.minWidth){
31234                 if(this.hidden){
31235                     this.el.beginMeasure();
31236                 }
31237                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31238                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31239                 }
31240                 if(this.hidden){
31241                     this.el.endMeasure();
31242                 }
31243             }
31244             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31245         } 
31246     },
31247     /**
31248      * Sets this button's click handler
31249      * @param {Function} handler The function to call when the button is clicked
31250      * @param {Object} scope (optional) Scope for the function passed above
31251      */
31252     setHandler : function(handler, scope){
31253         this.handler = handler;
31254         this.scope = scope;  
31255     },
31256     
31257     /**
31258      * Sets this button's arrow click handler
31259      * @param {Function} handler The function to call when the arrow is clicked
31260      * @param {Object} scope (optional) Scope for the function passed above
31261      */
31262     setArrowHandler : function(handler, scope){
31263         this.arrowHandler = handler;
31264         this.scope = scope;  
31265     },
31266     
31267     /**
31268      * Focus the button
31269      */
31270     focus : function(){
31271         if(this.el){
31272             this.el.child("button:first").focus();
31273         }
31274     },
31275
31276     // private
31277     onClick : function(e){
31278         e.preventDefault();
31279         if(!this.disabled){
31280             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31281                 if(this.menu && !this.menu.isVisible()){
31282                     this.menu.show(this.el, this.menuAlign);
31283                 }
31284                 this.fireEvent("arrowclick", this, e);
31285                 if(this.arrowHandler){
31286                     this.arrowHandler.call(this.scope || this, this, e);
31287                 }
31288             }else{
31289                 this.fireEvent("click", this, e);
31290                 if(this.handler){
31291                     this.handler.call(this.scope || this, this, e);
31292                 }
31293             }
31294         }
31295     },
31296     // private
31297     onMouseDown : function(e){
31298         if(!this.disabled){
31299             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31300         }
31301     },
31302     // private
31303     onMouseUp : function(e){
31304         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31305     }   
31306 });
31307
31308
31309 // backwards compat
31310 Roo.MenuButton = Roo.SplitButton;/*
31311  * Based on:
31312  * Ext JS Library 1.1.1
31313  * Copyright(c) 2006-2007, Ext JS, LLC.
31314  *
31315  * Originally Released Under LGPL - original licence link has changed is not relivant.
31316  *
31317  * Fork - LGPL
31318  * <script type="text/javascript">
31319  */
31320
31321 /**
31322  * @class Roo.Toolbar
31323  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31324  * Basic Toolbar class.
31325  * @constructor
31326  * Creates a new Toolbar
31327  * @param {Object} container The config object
31328  */ 
31329 Roo.Toolbar = function(container, buttons, config)
31330 {
31331     /// old consturctor format still supported..
31332     if(container instanceof Array){ // omit the container for later rendering
31333         buttons = container;
31334         config = buttons;
31335         container = null;
31336     }
31337     if (typeof(container) == 'object' && container.xtype) {
31338         config = container;
31339         container = config.container;
31340         buttons = config.buttons || []; // not really - use items!!
31341     }
31342     var xitems = [];
31343     if (config && config.items) {
31344         xitems = config.items;
31345         delete config.items;
31346     }
31347     Roo.apply(this, config);
31348     this.buttons = buttons;
31349     
31350     if(container){
31351         this.render(container);
31352     }
31353     this.xitems = xitems;
31354     Roo.each(xitems, function(b) {
31355         this.add(b);
31356     }, this);
31357     
31358 };
31359
31360 Roo.Toolbar.prototype = {
31361     /**
31362      * @cfg {Array} items
31363      * array of button configs or elements to add (will be converted to a MixedCollection)
31364      */
31365     items: false,
31366     /**
31367      * @cfg {String/HTMLElement/Element} container
31368      * The id or element that will contain the toolbar
31369      */
31370     // private
31371     render : function(ct){
31372         this.el = Roo.get(ct);
31373         if(this.cls){
31374             this.el.addClass(this.cls);
31375         }
31376         // using a table allows for vertical alignment
31377         // 100% width is needed by Safari...
31378         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31379         this.tr = this.el.child("tr", true);
31380         var autoId = 0;
31381         this.items = new Roo.util.MixedCollection(false, function(o){
31382             return o.id || ("item" + (++autoId));
31383         });
31384         if(this.buttons){
31385             this.add.apply(this, this.buttons);
31386             delete this.buttons;
31387         }
31388     },
31389
31390     /**
31391      * Adds element(s) to the toolbar -- this function takes a variable number of 
31392      * arguments of mixed type and adds them to the toolbar.
31393      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31394      * <ul>
31395      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31396      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31397      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31398      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31399      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31400      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31401      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31402      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31403      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31404      * </ul>
31405      * @param {Mixed} arg2
31406      * @param {Mixed} etc.
31407      */
31408     add : function(){
31409         var a = arguments, l = a.length;
31410         for(var i = 0; i < l; i++){
31411             this._add(a[i]);
31412         }
31413     },
31414     // private..
31415     _add : function(el) {
31416         
31417         if (el.xtype) {
31418             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31419         }
31420         
31421         if (el.applyTo){ // some kind of form field
31422             return this.addField(el);
31423         } 
31424         if (el.render){ // some kind of Toolbar.Item
31425             return this.addItem(el);
31426         }
31427         if (typeof el == "string"){ // string
31428             if(el == "separator" || el == "-"){
31429                 return this.addSeparator();
31430             }
31431             if (el == " "){
31432                 return this.addSpacer();
31433             }
31434             if(el == "->"){
31435                 return this.addFill();
31436             }
31437             return this.addText(el);
31438             
31439         }
31440         if(el.tagName){ // element
31441             return this.addElement(el);
31442         }
31443         if(typeof el == "object"){ // must be button config?
31444             return this.addButton(el);
31445         }
31446         // and now what?!?!
31447         return false;
31448         
31449     },
31450     
31451     /**
31452      * Add an Xtype element
31453      * @param {Object} xtype Xtype Object
31454      * @return {Object} created Object
31455      */
31456     addxtype : function(e){
31457         return this.add(e);  
31458     },
31459     
31460     /**
31461      * Returns the Element for this toolbar.
31462      * @return {Roo.Element}
31463      */
31464     getEl : function(){
31465         return this.el;  
31466     },
31467     
31468     /**
31469      * Adds a separator
31470      * @return {Roo.Toolbar.Item} The separator item
31471      */
31472     addSeparator : function(){
31473         return this.addItem(new Roo.Toolbar.Separator());
31474     },
31475
31476     /**
31477      * Adds a spacer element
31478      * @return {Roo.Toolbar.Spacer} The spacer item
31479      */
31480     addSpacer : function(){
31481         return this.addItem(new Roo.Toolbar.Spacer());
31482     },
31483
31484     /**
31485      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31486      * @return {Roo.Toolbar.Fill} The fill item
31487      */
31488     addFill : function(){
31489         return this.addItem(new Roo.Toolbar.Fill());
31490     },
31491
31492     /**
31493      * Adds any standard HTML element to the toolbar
31494      * @param {String/HTMLElement/Element} el The element or id of the element to add
31495      * @return {Roo.Toolbar.Item} The element's item
31496      */
31497     addElement : function(el){
31498         return this.addItem(new Roo.Toolbar.Item(el));
31499     },
31500     /**
31501      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31502      * @type Roo.util.MixedCollection  
31503      */
31504     items : false,
31505      
31506     /**
31507      * Adds any Toolbar.Item or subclass
31508      * @param {Roo.Toolbar.Item} item
31509      * @return {Roo.Toolbar.Item} The item
31510      */
31511     addItem : function(item){
31512         var td = this.nextBlock();
31513         item.render(td);
31514         this.items.add(item);
31515         return item;
31516     },
31517     
31518     /**
31519      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31520      * @param {Object/Array} config A button config or array of configs
31521      * @return {Roo.Toolbar.Button/Array}
31522      */
31523     addButton : function(config){
31524         if(config instanceof Array){
31525             var buttons = [];
31526             for(var i = 0, len = config.length; i < len; i++) {
31527                 buttons.push(this.addButton(config[i]));
31528             }
31529             return buttons;
31530         }
31531         var b = config;
31532         if(!(config instanceof Roo.Toolbar.Button)){
31533             b = config.split ?
31534                 new Roo.Toolbar.SplitButton(config) :
31535                 new Roo.Toolbar.Button(config);
31536         }
31537         var td = this.nextBlock();
31538         b.render(td);
31539         this.items.add(b);
31540         return b;
31541     },
31542     
31543     /**
31544      * Adds text to the toolbar
31545      * @param {String} text The text to add
31546      * @return {Roo.Toolbar.Item} The element's item
31547      */
31548     addText : function(text){
31549         return this.addItem(new Roo.Toolbar.TextItem(text));
31550     },
31551     
31552     /**
31553      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31554      * @param {Number} index The index where the item is to be inserted
31555      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31556      * @return {Roo.Toolbar.Button/Item}
31557      */
31558     insertButton : function(index, item){
31559         if(item instanceof Array){
31560             var buttons = [];
31561             for(var i = 0, len = item.length; i < len; i++) {
31562                buttons.push(this.insertButton(index + i, item[i]));
31563             }
31564             return buttons;
31565         }
31566         if (!(item instanceof Roo.Toolbar.Button)){
31567            item = new Roo.Toolbar.Button(item);
31568         }
31569         var td = document.createElement("td");
31570         this.tr.insertBefore(td, this.tr.childNodes[index]);
31571         item.render(td);
31572         this.items.insert(index, item);
31573         return item;
31574     },
31575     
31576     /**
31577      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31578      * @param {Object} config
31579      * @return {Roo.Toolbar.Item} The element's item
31580      */
31581     addDom : function(config, returnEl){
31582         var td = this.nextBlock();
31583         Roo.DomHelper.overwrite(td, config);
31584         var ti = new Roo.Toolbar.Item(td.firstChild);
31585         ti.render(td);
31586         this.items.add(ti);
31587         return ti;
31588     },
31589
31590     /**
31591      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31592      * @type Roo.util.MixedCollection  
31593      */
31594     fields : false,
31595     
31596     /**
31597      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31598      * Note: the field should not have been rendered yet. For a field that has already been
31599      * rendered, use {@link #addElement}.
31600      * @param {Roo.form.Field} field
31601      * @return {Roo.ToolbarItem}
31602      */
31603      
31604       
31605     addField : function(field) {
31606         if (!this.fields) {
31607             var autoId = 0;
31608             this.fields = new Roo.util.MixedCollection(false, function(o){
31609                 return o.id || ("item" + (++autoId));
31610             });
31611
31612         }
31613         
31614         var td = this.nextBlock();
31615         field.render(td);
31616         var ti = new Roo.Toolbar.Item(td.firstChild);
31617         ti.render(td);
31618         this.items.add(ti);
31619         this.fields.add(field);
31620         return ti;
31621     },
31622     /**
31623      * Hide the toolbar
31624      * @method hide
31625      */
31626      
31627       
31628     hide : function()
31629     {
31630         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31631         this.el.child('div').hide();
31632     },
31633     /**
31634      * Show the toolbar
31635      * @method show
31636      */
31637     show : function()
31638     {
31639         this.el.child('div').show();
31640     },
31641       
31642     // private
31643     nextBlock : function(){
31644         var td = document.createElement("td");
31645         this.tr.appendChild(td);
31646         return td;
31647     },
31648
31649     // private
31650     destroy : function(){
31651         if(this.items){ // rendered?
31652             Roo.destroy.apply(Roo, this.items.items);
31653         }
31654         if(this.fields){ // rendered?
31655             Roo.destroy.apply(Roo, this.fields.items);
31656         }
31657         Roo.Element.uncache(this.el, this.tr);
31658     }
31659 };
31660
31661 /**
31662  * @class Roo.Toolbar.Item
31663  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31664  * @constructor
31665  * Creates a new Item
31666  * @param {HTMLElement} el 
31667  */
31668 Roo.Toolbar.Item = function(el){
31669     var cfg = {};
31670     if (typeof (el.xtype) != 'undefined') {
31671         cfg = el;
31672         el = cfg.el;
31673     }
31674     
31675     this.el = Roo.getDom(el);
31676     this.id = Roo.id(this.el);
31677     this.hidden = false;
31678     
31679     this.addEvents({
31680          /**
31681              * @event render
31682              * Fires when the button is rendered
31683              * @param {Button} this
31684              */
31685         'render': true
31686     });
31687     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31688 };
31689 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31690 //Roo.Toolbar.Item.prototype = {
31691     
31692     /**
31693      * Get this item's HTML Element
31694      * @return {HTMLElement}
31695      */
31696     getEl : function(){
31697        return this.el;  
31698     },
31699
31700     // private
31701     render : function(td){
31702         
31703          this.td = td;
31704         td.appendChild(this.el);
31705         
31706         this.fireEvent('render', this);
31707     },
31708     
31709     /**
31710      * Removes and destroys this item.
31711      */
31712     destroy : function(){
31713         this.td.parentNode.removeChild(this.td);
31714     },
31715     
31716     /**
31717      * Shows this item.
31718      */
31719     show: function(){
31720         this.hidden = false;
31721         this.td.style.display = "";
31722     },
31723     
31724     /**
31725      * Hides this item.
31726      */
31727     hide: function(){
31728         this.hidden = true;
31729         this.td.style.display = "none";
31730     },
31731     
31732     /**
31733      * Convenience function for boolean show/hide.
31734      * @param {Boolean} visible true to show/false to hide
31735      */
31736     setVisible: function(visible){
31737         if(visible) {
31738             this.show();
31739         }else{
31740             this.hide();
31741         }
31742     },
31743     
31744     /**
31745      * Try to focus this item.
31746      */
31747     focus : function(){
31748         Roo.fly(this.el).focus();
31749     },
31750     
31751     /**
31752      * Disables this item.
31753      */
31754     disable : function(){
31755         Roo.fly(this.td).addClass("x-item-disabled");
31756         this.disabled = true;
31757         this.el.disabled = true;
31758     },
31759     
31760     /**
31761      * Enables this item.
31762      */
31763     enable : function(){
31764         Roo.fly(this.td).removeClass("x-item-disabled");
31765         this.disabled = false;
31766         this.el.disabled = false;
31767     }
31768 });
31769
31770
31771 /**
31772  * @class Roo.Toolbar.Separator
31773  * @extends Roo.Toolbar.Item
31774  * A simple toolbar separator class
31775  * @constructor
31776  * Creates a new Separator
31777  */
31778 Roo.Toolbar.Separator = function(cfg){
31779     
31780     var s = document.createElement("span");
31781     s.className = "ytb-sep";
31782     if (cfg) {
31783         cfg.el = s;
31784     }
31785     
31786     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31787 };
31788 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31789     enable:Roo.emptyFn,
31790     disable:Roo.emptyFn,
31791     focus:Roo.emptyFn
31792 });
31793
31794 /**
31795  * @class Roo.Toolbar.Spacer
31796  * @extends Roo.Toolbar.Item
31797  * A simple element that adds extra horizontal space to a toolbar.
31798  * @constructor
31799  * Creates a new Spacer
31800  */
31801 Roo.Toolbar.Spacer = function(cfg){
31802     var s = document.createElement("div");
31803     s.className = "ytb-spacer";
31804     if (cfg) {
31805         cfg.el = s;
31806     }
31807     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31808 };
31809 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31810     enable:Roo.emptyFn,
31811     disable:Roo.emptyFn,
31812     focus:Roo.emptyFn
31813 });
31814
31815 /**
31816  * @class Roo.Toolbar.Fill
31817  * @extends Roo.Toolbar.Spacer
31818  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31819  * @constructor
31820  * Creates a new Spacer
31821  */
31822 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31823     // private
31824     render : function(td){
31825         td.style.width = '100%';
31826         Roo.Toolbar.Fill.superclass.render.call(this, td);
31827     }
31828 });
31829
31830 /**
31831  * @class Roo.Toolbar.TextItem
31832  * @extends Roo.Toolbar.Item
31833  * A simple class that renders text directly into a toolbar.
31834  * @constructor
31835  * Creates a new TextItem
31836  * @cfg {string} text 
31837  */
31838 Roo.Toolbar.TextItem = function(cfg){
31839     var  text = cfg || "";
31840     if (typeof(cfg) == 'object') {
31841         text = cfg.text || "";
31842     }  else {
31843         cfg = null;
31844     }
31845     var s = document.createElement("span");
31846     s.className = "ytb-text";
31847     s.innerHTML = text;
31848     if (cfg) {
31849         cfg.el  = s;
31850     }
31851     
31852     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31853 };
31854 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31855     
31856      
31857     enable:Roo.emptyFn,
31858     disable:Roo.emptyFn,
31859     focus:Roo.emptyFn,
31860      /**
31861      * Shows this button
31862      */
31863     show: function(){
31864         this.hidden = false;
31865         this.el.style.display = "";
31866     },
31867     
31868     /**
31869      * Hides this button
31870      */
31871     hide: function(){
31872         this.hidden = true;
31873         this.el.style.display = "none";
31874     }
31875     
31876 });
31877
31878 /**
31879  * @class Roo.Toolbar.Button
31880  * @extends Roo.Button
31881  * A button that renders into a toolbar.
31882  * @constructor
31883  * Creates a new Button
31884  * @param {Object} config A standard {@link Roo.Button} config object
31885  */
31886 Roo.Toolbar.Button = function(config){
31887     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31888 };
31889 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31890 {
31891     
31892     
31893     render : function(td){
31894         this.td = td;
31895         Roo.Toolbar.Button.superclass.render.call(this, td);
31896     },
31897     
31898     /**
31899      * Removes and destroys this button
31900      */
31901     destroy : function(){
31902         Roo.Toolbar.Button.superclass.destroy.call(this);
31903         this.td.parentNode.removeChild(this.td);
31904     },
31905     
31906     /**
31907      * Shows this button
31908      */
31909     show: function(){
31910         this.hidden = false;
31911         this.td.style.display = "";
31912     },
31913     
31914     /**
31915      * Hides this button
31916      */
31917     hide: function(){
31918         this.hidden = true;
31919         this.td.style.display = "none";
31920     },
31921
31922     /**
31923      * Disables this item
31924      */
31925     disable : function(){
31926         Roo.fly(this.td).addClass("x-item-disabled");
31927         this.disabled = true;
31928     },
31929
31930     /**
31931      * Enables this item
31932      */
31933     enable : function(){
31934         Roo.fly(this.td).removeClass("x-item-disabled");
31935         this.disabled = false;
31936     }
31937 });
31938 // backwards compat
31939 Roo.ToolbarButton = Roo.Toolbar.Button;
31940
31941 /**
31942  * @class Roo.Toolbar.SplitButton
31943  * @extends Roo.SplitButton
31944  * A menu button that renders into a toolbar.
31945  * @constructor
31946  * Creates a new SplitButton
31947  * @param {Object} config A standard {@link Roo.SplitButton} config object
31948  */
31949 Roo.Toolbar.SplitButton = function(config){
31950     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31951 };
31952 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31953     render : function(td){
31954         this.td = td;
31955         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31956     },
31957     
31958     /**
31959      * Removes and destroys this button
31960      */
31961     destroy : function(){
31962         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31963         this.td.parentNode.removeChild(this.td);
31964     },
31965     
31966     /**
31967      * Shows this button
31968      */
31969     show: function(){
31970         this.hidden = false;
31971         this.td.style.display = "";
31972     },
31973     
31974     /**
31975      * Hides this button
31976      */
31977     hide: function(){
31978         this.hidden = true;
31979         this.td.style.display = "none";
31980     }
31981 });
31982
31983 // backwards compat
31984 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31985  * Based on:
31986  * Ext JS Library 1.1.1
31987  * Copyright(c) 2006-2007, Ext JS, LLC.
31988  *
31989  * Originally Released Under LGPL - original licence link has changed is not relivant.
31990  *
31991  * Fork - LGPL
31992  * <script type="text/javascript">
31993  */
31994  
31995 /**
31996  * @class Roo.PagingToolbar
31997  * @extends Roo.Toolbar
31998  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31999  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32000  * @constructor
32001  * Create a new PagingToolbar
32002  * @param {Object} config The config object
32003  */
32004 Roo.PagingToolbar = function(el, ds, config)
32005 {
32006     // old args format still supported... - xtype is prefered..
32007     if (typeof(el) == 'object' && el.xtype) {
32008         // created from xtype...
32009         config = el;
32010         ds = el.dataSource;
32011         el = config.container;
32012     }
32013     var items = [];
32014     if (config.items) {
32015         items = config.items;
32016         config.items = [];
32017     }
32018     
32019     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32020     this.ds = ds;
32021     this.cursor = 0;
32022     this.renderButtons(this.el);
32023     this.bind(ds);
32024     
32025     // supprot items array.
32026    
32027     Roo.each(items, function(e) {
32028         this.add(Roo.factory(e));
32029     },this);
32030     
32031 };
32032
32033 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32034    
32035     /**
32036      * @cfg {String/HTMLElement/Element} container
32037      * container The id or element that will contain the toolbar
32038      */
32039     /**
32040      * @cfg {Boolean} displayInfo
32041      * True to display the displayMsg (defaults to false)
32042      */
32043     
32044     
32045     /**
32046      * @cfg {Number} pageSize
32047      * The number of records to display per page (defaults to 20)
32048      */
32049     pageSize: 20,
32050     /**
32051      * @cfg {String} displayMsg
32052      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32053      */
32054     displayMsg : 'Displaying {0} - {1} of {2}',
32055     /**
32056      * @cfg {String} emptyMsg
32057      * The message to display when no records are found (defaults to "No data to display")
32058      */
32059     emptyMsg : 'No data to display',
32060     /**
32061      * Customizable piece of the default paging text (defaults to "Page")
32062      * @type String
32063      */
32064     beforePageText : "Page",
32065     /**
32066      * Customizable piece of the default paging text (defaults to "of %0")
32067      * @type String
32068      */
32069     afterPageText : "of {0}",
32070     /**
32071      * Customizable piece of the default paging text (defaults to "First Page")
32072      * @type String
32073      */
32074     firstText : "First Page",
32075     /**
32076      * Customizable piece of the default paging text (defaults to "Previous Page")
32077      * @type String
32078      */
32079     prevText : "Previous Page",
32080     /**
32081      * Customizable piece of the default paging text (defaults to "Next Page")
32082      * @type String
32083      */
32084     nextText : "Next Page",
32085     /**
32086      * Customizable piece of the default paging text (defaults to "Last Page")
32087      * @type String
32088      */
32089     lastText : "Last Page",
32090     /**
32091      * Customizable piece of the default paging text (defaults to "Refresh")
32092      * @type String
32093      */
32094     refreshText : "Refresh",
32095
32096     // private
32097     renderButtons : function(el){
32098         Roo.PagingToolbar.superclass.render.call(this, el);
32099         this.first = this.addButton({
32100             tooltip: this.firstText,
32101             cls: "x-btn-icon x-grid-page-first",
32102             disabled: true,
32103             handler: this.onClick.createDelegate(this, ["first"])
32104         });
32105         this.prev = this.addButton({
32106             tooltip: this.prevText,
32107             cls: "x-btn-icon x-grid-page-prev",
32108             disabled: true,
32109             handler: this.onClick.createDelegate(this, ["prev"])
32110         });
32111         //this.addSeparator();
32112         this.add(this.beforePageText);
32113         this.field = Roo.get(this.addDom({
32114            tag: "input",
32115            type: "text",
32116            size: "3",
32117            value: "1",
32118            cls: "x-grid-page-number"
32119         }).el);
32120         this.field.on("keydown", this.onPagingKeydown, this);
32121         this.field.on("focus", function(){this.dom.select();});
32122         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32123         this.field.setHeight(18);
32124         //this.addSeparator();
32125         this.next = this.addButton({
32126             tooltip: this.nextText,
32127             cls: "x-btn-icon x-grid-page-next",
32128             disabled: true,
32129             handler: this.onClick.createDelegate(this, ["next"])
32130         });
32131         this.last = this.addButton({
32132             tooltip: this.lastText,
32133             cls: "x-btn-icon x-grid-page-last",
32134             disabled: true,
32135             handler: this.onClick.createDelegate(this, ["last"])
32136         });
32137         //this.addSeparator();
32138         this.loading = this.addButton({
32139             tooltip: this.refreshText,
32140             cls: "x-btn-icon x-grid-loading",
32141             handler: this.onClick.createDelegate(this, ["refresh"])
32142         });
32143
32144         if(this.displayInfo){
32145             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32146         }
32147     },
32148
32149     // private
32150     updateInfo : function(){
32151         if(this.displayEl){
32152             var count = this.ds.getCount();
32153             var msg = count == 0 ?
32154                 this.emptyMsg :
32155                 String.format(
32156                     this.displayMsg,
32157                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32158                 );
32159             this.displayEl.update(msg);
32160         }
32161     },
32162
32163     // private
32164     onLoad : function(ds, r, o){
32165        this.cursor = o.params ? o.params.start : 0;
32166        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32167
32168        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32169        this.field.dom.value = ap;
32170        this.first.setDisabled(ap == 1);
32171        this.prev.setDisabled(ap == 1);
32172        this.next.setDisabled(ap == ps);
32173        this.last.setDisabled(ap == ps);
32174        this.loading.enable();
32175        this.updateInfo();
32176     },
32177
32178     // private
32179     getPageData : function(){
32180         var total = this.ds.getTotalCount();
32181         return {
32182             total : total,
32183             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32184             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32185         };
32186     },
32187
32188     // private
32189     onLoadError : function(){
32190         this.loading.enable();
32191     },
32192
32193     // private
32194     onPagingKeydown : function(e){
32195         var k = e.getKey();
32196         var d = this.getPageData();
32197         if(k == e.RETURN){
32198             var v = this.field.dom.value, pageNum;
32199             if(!v || isNaN(pageNum = parseInt(v, 10))){
32200                 this.field.dom.value = d.activePage;
32201                 return;
32202             }
32203             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32204             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32205             e.stopEvent();
32206         }
32207         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))
32208         {
32209           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32210           this.field.dom.value = pageNum;
32211           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32212           e.stopEvent();
32213         }
32214         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32215         {
32216           var v = this.field.dom.value, pageNum; 
32217           var increment = (e.shiftKey) ? 10 : 1;
32218           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32219             increment *= -1;
32220           }
32221           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32222             this.field.dom.value = d.activePage;
32223             return;
32224           }
32225           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32226           {
32227             this.field.dom.value = parseInt(v, 10) + increment;
32228             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32229             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32230           }
32231           e.stopEvent();
32232         }
32233     },
32234
32235     // private
32236     beforeLoad : function(){
32237         if(this.loading){
32238             this.loading.disable();
32239         }
32240     },
32241
32242     // private
32243     onClick : function(which){
32244         var ds = this.ds;
32245         switch(which){
32246             case "first":
32247                 ds.load({params:{start: 0, limit: this.pageSize}});
32248             break;
32249             case "prev":
32250                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32251             break;
32252             case "next":
32253                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32254             break;
32255             case "last":
32256                 var total = ds.getTotalCount();
32257                 var extra = total % this.pageSize;
32258                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32259                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32260             break;
32261             case "refresh":
32262                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32263             break;
32264         }
32265     },
32266
32267     /**
32268      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32269      * @param {Roo.data.Store} store The data store to unbind
32270      */
32271     unbind : function(ds){
32272         ds.un("beforeload", this.beforeLoad, this);
32273         ds.un("load", this.onLoad, this);
32274         ds.un("loadexception", this.onLoadError, this);
32275         ds.un("remove", this.updateInfo, this);
32276         ds.un("add", this.updateInfo, this);
32277         this.ds = undefined;
32278     },
32279
32280     /**
32281      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32282      * @param {Roo.data.Store} store The data store to bind
32283      */
32284     bind : function(ds){
32285         ds.on("beforeload", this.beforeLoad, this);
32286         ds.on("load", this.onLoad, this);
32287         ds.on("loadexception", this.onLoadError, this);
32288         ds.on("remove", this.updateInfo, this);
32289         ds.on("add", this.updateInfo, this);
32290         this.ds = ds;
32291     }
32292 });/*
32293  * Based on:
32294  * Ext JS Library 1.1.1
32295  * Copyright(c) 2006-2007, Ext JS, LLC.
32296  *
32297  * Originally Released Under LGPL - original licence link has changed is not relivant.
32298  *
32299  * Fork - LGPL
32300  * <script type="text/javascript">
32301  */
32302
32303 /**
32304  * @class Roo.Resizable
32305  * @extends Roo.util.Observable
32306  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32307  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32308  * 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
32309  * the element will be wrapped for you automatically.</p>
32310  * <p>Here is the list of valid resize handles:</p>
32311  * <pre>
32312 Value   Description
32313 ------  -------------------
32314  'n'     north
32315  's'     south
32316  'e'     east
32317  'w'     west
32318  'nw'    northwest
32319  'sw'    southwest
32320  'se'    southeast
32321  'ne'    northeast
32322  'hd'    horizontal drag
32323  'all'   all
32324 </pre>
32325  * <p>Here's an example showing the creation of a typical Resizable:</p>
32326  * <pre><code>
32327 var resizer = new Roo.Resizable("element-id", {
32328     handles: 'all',
32329     minWidth: 200,
32330     minHeight: 100,
32331     maxWidth: 500,
32332     maxHeight: 400,
32333     pinned: true
32334 });
32335 resizer.on("resize", myHandler);
32336 </code></pre>
32337  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32338  * resizer.east.setDisplayed(false);</p>
32339  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32340  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32341  * resize operation's new size (defaults to [0, 0])
32342  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32343  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32344  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32345  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32346  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32347  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32348  * @cfg {Number} width The width of the element in pixels (defaults to null)
32349  * @cfg {Number} height The height of the element in pixels (defaults to null)
32350  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32351  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32352  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32353  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32354  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32355  * in favor of the handles config option (defaults to false)
32356  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32357  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32358  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32359  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32360  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32361  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32362  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32363  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32364  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32365  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32366  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32367  * @constructor
32368  * Create a new resizable component
32369  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32370  * @param {Object} config configuration options
32371   */
32372 Roo.Resizable = function(el, config)
32373 {
32374     this.el = Roo.get(el);
32375
32376     if(config && config.wrap){
32377         config.resizeChild = this.el;
32378         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32379         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32380         this.el.setStyle("overflow", "hidden");
32381         this.el.setPositioning(config.resizeChild.getPositioning());
32382         config.resizeChild.clearPositioning();
32383         if(!config.width || !config.height){
32384             var csize = config.resizeChild.getSize();
32385             this.el.setSize(csize.width, csize.height);
32386         }
32387         if(config.pinned && !config.adjustments){
32388             config.adjustments = "auto";
32389         }
32390     }
32391
32392     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32393     this.proxy.unselectable();
32394     this.proxy.enableDisplayMode('block');
32395
32396     Roo.apply(this, config);
32397
32398     if(this.pinned){
32399         this.disableTrackOver = true;
32400         this.el.addClass("x-resizable-pinned");
32401     }
32402     // if the element isn't positioned, make it relative
32403     var position = this.el.getStyle("position");
32404     if(position != "absolute" && position != "fixed"){
32405         this.el.setStyle("position", "relative");
32406     }
32407     if(!this.handles){ // no handles passed, must be legacy style
32408         this.handles = 's,e,se';
32409         if(this.multiDirectional){
32410             this.handles += ',n,w';
32411         }
32412     }
32413     if(this.handles == "all"){
32414         this.handles = "n s e w ne nw se sw";
32415     }
32416     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32417     var ps = Roo.Resizable.positions;
32418     for(var i = 0, len = hs.length; i < len; i++){
32419         if(hs[i] && ps[hs[i]]){
32420             var pos = ps[hs[i]];
32421             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32422         }
32423     }
32424     // legacy
32425     this.corner = this.southeast;
32426     
32427     // updateBox = the box can move..
32428     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32429         this.updateBox = true;
32430     }
32431
32432     this.activeHandle = null;
32433
32434     if(this.resizeChild){
32435         if(typeof this.resizeChild == "boolean"){
32436             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32437         }else{
32438             this.resizeChild = Roo.get(this.resizeChild, true);
32439         }
32440     }
32441     
32442     if(this.adjustments == "auto"){
32443         var rc = this.resizeChild;
32444         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32445         if(rc && (hw || hn)){
32446             rc.position("relative");
32447             rc.setLeft(hw ? hw.el.getWidth() : 0);
32448             rc.setTop(hn ? hn.el.getHeight() : 0);
32449         }
32450         this.adjustments = [
32451             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32452             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32453         ];
32454     }
32455
32456     if(this.draggable){
32457         this.dd = this.dynamic ?
32458             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32459         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32460     }
32461
32462     // public events
32463     this.addEvents({
32464         /**
32465          * @event beforeresize
32466          * Fired before resize is allowed. Set enabled to false to cancel resize.
32467          * @param {Roo.Resizable} this
32468          * @param {Roo.EventObject} e The mousedown event
32469          */
32470         "beforeresize" : true,
32471         /**
32472          * @event resizing
32473          * Fired a resizing.
32474          * @param {Roo.Resizable} this
32475          * @param {Number} x The new x position
32476          * @param {Number} y The new y position
32477          * @param {Number} w The new w width
32478          * @param {Number} h The new h hight
32479          * @param {Roo.EventObject} e The mouseup event
32480          */
32481         "resizing" : true,
32482         /**
32483          * @event resize
32484          * Fired after a resize.
32485          * @param {Roo.Resizable} this
32486          * @param {Number} width The new width
32487          * @param {Number} height The new height
32488          * @param {Roo.EventObject} e The mouseup event
32489          */
32490         "resize" : true
32491     });
32492
32493     if(this.width !== null && this.height !== null){
32494         this.resizeTo(this.width, this.height);
32495     }else{
32496         this.updateChildSize();
32497     }
32498     if(Roo.isIE){
32499         this.el.dom.style.zoom = 1;
32500     }
32501     Roo.Resizable.superclass.constructor.call(this);
32502 };
32503
32504 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32505         resizeChild : false,
32506         adjustments : [0, 0],
32507         minWidth : 5,
32508         minHeight : 5,
32509         maxWidth : 10000,
32510         maxHeight : 10000,
32511         enabled : true,
32512         animate : false,
32513         duration : .35,
32514         dynamic : false,
32515         handles : false,
32516         multiDirectional : false,
32517         disableTrackOver : false,
32518         easing : 'easeOutStrong',
32519         widthIncrement : 0,
32520         heightIncrement : 0,
32521         pinned : false,
32522         width : null,
32523         height : null,
32524         preserveRatio : false,
32525         transparent: false,
32526         minX: 0,
32527         minY: 0,
32528         draggable: false,
32529
32530         /**
32531          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32532          */
32533         constrainTo: undefined,
32534         /**
32535          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32536          */
32537         resizeRegion: undefined,
32538
32539
32540     /**
32541      * Perform a manual resize
32542      * @param {Number} width
32543      * @param {Number} height
32544      */
32545     resizeTo : function(width, height){
32546         this.el.setSize(width, height);
32547         this.updateChildSize();
32548         this.fireEvent("resize", this, width, height, null);
32549     },
32550
32551     // private
32552     startSizing : function(e, handle){
32553         this.fireEvent("beforeresize", this, e);
32554         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32555
32556             if(!this.overlay){
32557                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32558                 this.overlay.unselectable();
32559                 this.overlay.enableDisplayMode("block");
32560                 this.overlay.on("mousemove", this.onMouseMove, this);
32561                 this.overlay.on("mouseup", this.onMouseUp, this);
32562             }
32563             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32564
32565             this.resizing = true;
32566             this.startBox = this.el.getBox();
32567             this.startPoint = e.getXY();
32568             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32569                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32570
32571             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32572             this.overlay.show();
32573
32574             if(this.constrainTo) {
32575                 var ct = Roo.get(this.constrainTo);
32576                 this.resizeRegion = ct.getRegion().adjust(
32577                     ct.getFrameWidth('t'),
32578                     ct.getFrameWidth('l'),
32579                     -ct.getFrameWidth('b'),
32580                     -ct.getFrameWidth('r')
32581                 );
32582             }
32583
32584             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32585             this.proxy.show();
32586             this.proxy.setBox(this.startBox);
32587             if(!this.dynamic){
32588                 this.proxy.setStyle('visibility', 'visible');
32589             }
32590         }
32591     },
32592
32593     // private
32594     onMouseDown : function(handle, e){
32595         if(this.enabled){
32596             e.stopEvent();
32597             this.activeHandle = handle;
32598             this.startSizing(e, handle);
32599         }
32600     },
32601
32602     // private
32603     onMouseUp : function(e){
32604         var size = this.resizeElement();
32605         this.resizing = false;
32606         this.handleOut();
32607         this.overlay.hide();
32608         this.proxy.hide();
32609         this.fireEvent("resize", this, size.width, size.height, e);
32610     },
32611
32612     // private
32613     updateChildSize : function(){
32614         
32615         if(this.resizeChild){
32616             var el = this.el;
32617             var child = this.resizeChild;
32618             var adj = this.adjustments;
32619             if(el.dom.offsetWidth){
32620                 var b = el.getSize(true);
32621                 child.setSize(b.width+adj[0], b.height+adj[1]);
32622             }
32623             // Second call here for IE
32624             // The first call enables instant resizing and
32625             // the second call corrects scroll bars if they
32626             // exist
32627             if(Roo.isIE){
32628                 setTimeout(function(){
32629                     if(el.dom.offsetWidth){
32630                         var b = el.getSize(true);
32631                         child.setSize(b.width+adj[0], b.height+adj[1]);
32632                     }
32633                 }, 10);
32634             }
32635         }
32636     },
32637
32638     // private
32639     snap : function(value, inc, min){
32640         if(!inc || !value) {
32641             return value;
32642         }
32643         var newValue = value;
32644         var m = value % inc;
32645         if(m > 0){
32646             if(m > (inc/2)){
32647                 newValue = value + (inc-m);
32648             }else{
32649                 newValue = value - m;
32650             }
32651         }
32652         return Math.max(min, newValue);
32653     },
32654
32655     // private
32656     resizeElement : function(){
32657         var box = this.proxy.getBox();
32658         if(this.updateBox){
32659             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32660         }else{
32661             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32662         }
32663         this.updateChildSize();
32664         if(!this.dynamic){
32665             this.proxy.hide();
32666         }
32667         return box;
32668     },
32669
32670     // private
32671     constrain : function(v, diff, m, mx){
32672         if(v - diff < m){
32673             diff = v - m;
32674         }else if(v - diff > mx){
32675             diff = mx - v;
32676         }
32677         return diff;
32678     },
32679
32680     // private
32681     onMouseMove : function(e){
32682         
32683         if(this.enabled){
32684             try{// try catch so if something goes wrong the user doesn't get hung
32685
32686             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32687                 return;
32688             }
32689
32690             //var curXY = this.startPoint;
32691             var curSize = this.curSize || this.startBox;
32692             var x = this.startBox.x, y = this.startBox.y;
32693             var ox = x, oy = y;
32694             var w = curSize.width, h = curSize.height;
32695             var ow = w, oh = h;
32696             var mw = this.minWidth, mh = this.minHeight;
32697             var mxw = this.maxWidth, mxh = this.maxHeight;
32698             var wi = this.widthIncrement;
32699             var hi = this.heightIncrement;
32700
32701             var eventXY = e.getXY();
32702             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32703             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32704
32705             var pos = this.activeHandle.position;
32706
32707             switch(pos){
32708                 case "east":
32709                     w += diffX;
32710                     w = Math.min(Math.max(mw, w), mxw);
32711                     break;
32712              
32713                 case "south":
32714                     h += diffY;
32715                     h = Math.min(Math.max(mh, h), mxh);
32716                     break;
32717                 case "southeast":
32718                     w += diffX;
32719                     h += diffY;
32720                     w = Math.min(Math.max(mw, w), mxw);
32721                     h = Math.min(Math.max(mh, h), mxh);
32722                     break;
32723                 case "north":
32724                     diffY = this.constrain(h, diffY, mh, mxh);
32725                     y += diffY;
32726                     h -= diffY;
32727                     break;
32728                 case "hdrag":
32729                     
32730                     if (wi) {
32731                         var adiffX = Math.abs(diffX);
32732                         var sub = (adiffX % wi); // how much 
32733                         if (sub > (wi/2)) { // far enough to snap
32734                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32735                         } else {
32736                             // remove difference.. 
32737                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32738                         }
32739                     }
32740                     x += diffX;
32741                     x = Math.max(this.minX, x);
32742                     break;
32743                 case "west":
32744                     diffX = this.constrain(w, diffX, mw, mxw);
32745                     x += diffX;
32746                     w -= diffX;
32747                     break;
32748                 case "northeast":
32749                     w += diffX;
32750                     w = Math.min(Math.max(mw, w), mxw);
32751                     diffY = this.constrain(h, diffY, mh, mxh);
32752                     y += diffY;
32753                     h -= diffY;
32754                     break;
32755                 case "northwest":
32756                     diffX = this.constrain(w, diffX, mw, mxw);
32757                     diffY = this.constrain(h, diffY, mh, mxh);
32758                     y += diffY;
32759                     h -= diffY;
32760                     x += diffX;
32761                     w -= diffX;
32762                     break;
32763                case "southwest":
32764                     diffX = this.constrain(w, diffX, mw, mxw);
32765                     h += diffY;
32766                     h = Math.min(Math.max(mh, h), mxh);
32767                     x += diffX;
32768                     w -= diffX;
32769                     break;
32770             }
32771
32772             var sw = this.snap(w, wi, mw);
32773             var sh = this.snap(h, hi, mh);
32774             if(sw != w || sh != h){
32775                 switch(pos){
32776                     case "northeast":
32777                         y -= sh - h;
32778                     break;
32779                     case "north":
32780                         y -= sh - h;
32781                         break;
32782                     case "southwest":
32783                         x -= sw - w;
32784                     break;
32785                     case "west":
32786                         x -= sw - w;
32787                         break;
32788                     case "northwest":
32789                         x -= sw - w;
32790                         y -= sh - h;
32791                     break;
32792                 }
32793                 w = sw;
32794                 h = sh;
32795             }
32796
32797             if(this.preserveRatio){
32798                 switch(pos){
32799                     case "southeast":
32800                     case "east":
32801                         h = oh * (w/ow);
32802                         h = Math.min(Math.max(mh, h), mxh);
32803                         w = ow * (h/oh);
32804                        break;
32805                     case "south":
32806                         w = ow * (h/oh);
32807                         w = Math.min(Math.max(mw, w), mxw);
32808                         h = oh * (w/ow);
32809                         break;
32810                     case "northeast":
32811                         w = ow * (h/oh);
32812                         w = Math.min(Math.max(mw, w), mxw);
32813                         h = oh * (w/ow);
32814                     break;
32815                     case "north":
32816                         var tw = w;
32817                         w = ow * (h/oh);
32818                         w = Math.min(Math.max(mw, w), mxw);
32819                         h = oh * (w/ow);
32820                         x += (tw - w) / 2;
32821                         break;
32822                     case "southwest":
32823                         h = oh * (w/ow);
32824                         h = Math.min(Math.max(mh, h), mxh);
32825                         var tw = w;
32826                         w = ow * (h/oh);
32827                         x += tw - w;
32828                         break;
32829                     case "west":
32830                         var th = h;
32831                         h = oh * (w/ow);
32832                         h = Math.min(Math.max(mh, h), mxh);
32833                         y += (th - h) / 2;
32834                         var tw = w;
32835                         w = ow * (h/oh);
32836                         x += tw - w;
32837                        break;
32838                     case "northwest":
32839                         var tw = w;
32840                         var th = h;
32841                         h = oh * (w/ow);
32842                         h = Math.min(Math.max(mh, h), mxh);
32843                         w = ow * (h/oh);
32844                         y += th - h;
32845                         x += tw - w;
32846                        break;
32847
32848                 }
32849             }
32850             if (pos == 'hdrag') {
32851                 w = ow;
32852             }
32853             this.proxy.setBounds(x, y, w, h);
32854             if(this.dynamic){
32855                 this.resizeElement();
32856             }
32857             }catch(e){}
32858         }
32859         this.fireEvent("resizing", this, x, y, w, h, e);
32860     },
32861
32862     // private
32863     handleOver : function(){
32864         if(this.enabled){
32865             this.el.addClass("x-resizable-over");
32866         }
32867     },
32868
32869     // private
32870     handleOut : function(){
32871         if(!this.resizing){
32872             this.el.removeClass("x-resizable-over");
32873         }
32874     },
32875
32876     /**
32877      * Returns the element this component is bound to.
32878      * @return {Roo.Element}
32879      */
32880     getEl : function(){
32881         return this.el;
32882     },
32883
32884     /**
32885      * Returns the resizeChild element (or null).
32886      * @return {Roo.Element}
32887      */
32888     getResizeChild : function(){
32889         return this.resizeChild;
32890     },
32891     groupHandler : function()
32892     {
32893         
32894     },
32895     /**
32896      * Destroys this resizable. If the element was wrapped and
32897      * removeEl is not true then the element remains.
32898      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32899      */
32900     destroy : function(removeEl){
32901         this.proxy.remove();
32902         if(this.overlay){
32903             this.overlay.removeAllListeners();
32904             this.overlay.remove();
32905         }
32906         var ps = Roo.Resizable.positions;
32907         for(var k in ps){
32908             if(typeof ps[k] != "function" && this[ps[k]]){
32909                 var h = this[ps[k]];
32910                 h.el.removeAllListeners();
32911                 h.el.remove();
32912             }
32913         }
32914         if(removeEl){
32915             this.el.update("");
32916             this.el.remove();
32917         }
32918     }
32919 });
32920
32921 // private
32922 // hash to map config positions to true positions
32923 Roo.Resizable.positions = {
32924     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32925     hd: "hdrag"
32926 };
32927
32928 // private
32929 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32930     if(!this.tpl){
32931         // only initialize the template if resizable is used
32932         var tpl = Roo.DomHelper.createTemplate(
32933             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32934         );
32935         tpl.compile();
32936         Roo.Resizable.Handle.prototype.tpl = tpl;
32937     }
32938     this.position = pos;
32939     this.rz = rz;
32940     // show north drag fro topdra
32941     var handlepos = pos == 'hdrag' ? 'north' : pos;
32942     
32943     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32944     if (pos == 'hdrag') {
32945         this.el.setStyle('cursor', 'pointer');
32946     }
32947     this.el.unselectable();
32948     if(transparent){
32949         this.el.setOpacity(0);
32950     }
32951     this.el.on("mousedown", this.onMouseDown, this);
32952     if(!disableTrackOver){
32953         this.el.on("mouseover", this.onMouseOver, this);
32954         this.el.on("mouseout", this.onMouseOut, this);
32955     }
32956 };
32957
32958 // private
32959 Roo.Resizable.Handle.prototype = {
32960     afterResize : function(rz){
32961         Roo.log('after?');
32962         // do nothing
32963     },
32964     // private
32965     onMouseDown : function(e){
32966         this.rz.onMouseDown(this, e);
32967     },
32968     // private
32969     onMouseOver : function(e){
32970         this.rz.handleOver(this, e);
32971     },
32972     // private
32973     onMouseOut : function(e){
32974         this.rz.handleOut(this, e);
32975     }
32976 };/*
32977  * Based on:
32978  * Ext JS Library 1.1.1
32979  * Copyright(c) 2006-2007, Ext JS, LLC.
32980  *
32981  * Originally Released Under LGPL - original licence link has changed is not relivant.
32982  *
32983  * Fork - LGPL
32984  * <script type="text/javascript">
32985  */
32986
32987 /**
32988  * @class Roo.Editor
32989  * @extends Roo.Component
32990  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32991  * @constructor
32992  * Create a new Editor
32993  * @param {Roo.form.Field} field The Field object (or descendant)
32994  * @param {Object} config The config object
32995  */
32996 Roo.Editor = function(field, config){
32997     Roo.Editor.superclass.constructor.call(this, config);
32998     this.field = field;
32999     this.addEvents({
33000         /**
33001              * @event beforestartedit
33002              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
33003              * false from the handler of this event.
33004              * @param {Editor} this
33005              * @param {Roo.Element} boundEl The underlying element bound to this editor
33006              * @param {Mixed} value The field value being set
33007              */
33008         "beforestartedit" : true,
33009         /**
33010              * @event startedit
33011              * Fires when this editor is displayed
33012              * @param {Roo.Element} boundEl The underlying element bound to this editor
33013              * @param {Mixed} value The starting field value
33014              */
33015         "startedit" : true,
33016         /**
33017              * @event beforecomplete
33018              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33019              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33020              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33021              * event will not fire since no edit actually occurred.
33022              * @param {Editor} this
33023              * @param {Mixed} value The current field value
33024              * @param {Mixed} startValue The original field value
33025              */
33026         "beforecomplete" : true,
33027         /**
33028              * @event complete
33029              * Fires after editing is complete and any changed value has been written to the underlying field.
33030              * @param {Editor} this
33031              * @param {Mixed} value The current field value
33032              * @param {Mixed} startValue The original field value
33033              */
33034         "complete" : true,
33035         /**
33036          * @event specialkey
33037          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33038          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33039          * @param {Roo.form.Field} this
33040          * @param {Roo.EventObject} e The event object
33041          */
33042         "specialkey" : true
33043     });
33044 };
33045
33046 Roo.extend(Roo.Editor, Roo.Component, {
33047     /**
33048      * @cfg {Boolean/String} autosize
33049      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33050      * or "height" to adopt the height only (defaults to false)
33051      */
33052     /**
33053      * @cfg {Boolean} revertInvalid
33054      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33055      * validation fails (defaults to true)
33056      */
33057     /**
33058      * @cfg {Boolean} ignoreNoChange
33059      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33060      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33061      * will never be ignored.
33062      */
33063     /**
33064      * @cfg {Boolean} hideEl
33065      * False to keep the bound element visible while the editor is displayed (defaults to true)
33066      */
33067     /**
33068      * @cfg {Mixed} value
33069      * The data value of the underlying field (defaults to "")
33070      */
33071     value : "",
33072     /**
33073      * @cfg {String} alignment
33074      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33075      */
33076     alignment: "c-c?",
33077     /**
33078      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33079      * for bottom-right shadow (defaults to "frame")
33080      */
33081     shadow : "frame",
33082     /**
33083      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33084      */
33085     constrain : false,
33086     /**
33087      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33088      */
33089     completeOnEnter : false,
33090     /**
33091      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33092      */
33093     cancelOnEsc : false,
33094     /**
33095      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33096      */
33097     updateEl : false,
33098
33099     // private
33100     onRender : function(ct, position){
33101         this.el = new Roo.Layer({
33102             shadow: this.shadow,
33103             cls: "x-editor",
33104             parentEl : ct,
33105             shim : this.shim,
33106             shadowOffset:4,
33107             id: this.id,
33108             constrain: this.constrain
33109         });
33110         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33111         if(this.field.msgTarget != 'title'){
33112             this.field.msgTarget = 'qtip';
33113         }
33114         this.field.render(this.el);
33115         if(Roo.isGecko){
33116             this.field.el.dom.setAttribute('autocomplete', 'off');
33117         }
33118         this.field.on("specialkey", this.onSpecialKey, this);
33119         if(this.swallowKeys){
33120             this.field.el.swallowEvent(['keydown','keypress']);
33121         }
33122         this.field.show();
33123         this.field.on("blur", this.onBlur, this);
33124         if(this.field.grow){
33125             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33126         }
33127     },
33128
33129     onSpecialKey : function(field, e)
33130     {
33131         //Roo.log('editor onSpecialKey');
33132         if(this.completeOnEnter && e.getKey() == e.ENTER){
33133             e.stopEvent();
33134             this.completeEdit();
33135             return;
33136         }
33137         // do not fire special key otherwise it might hide close the editor...
33138         if(e.getKey() == e.ENTER){    
33139             return;
33140         }
33141         if(this.cancelOnEsc && e.getKey() == e.ESC){
33142             this.cancelEdit();
33143             return;
33144         } 
33145         this.fireEvent('specialkey', field, e);
33146     
33147     },
33148
33149     /**
33150      * Starts the editing process and shows the editor.
33151      * @param {String/HTMLElement/Element} el The element to edit
33152      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33153       * to the innerHTML of el.
33154      */
33155     startEdit : function(el, value){
33156         if(this.editing){
33157             this.completeEdit();
33158         }
33159         this.boundEl = Roo.get(el);
33160         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33161         if(!this.rendered){
33162             this.render(this.parentEl || document.body);
33163         }
33164         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33165             return;
33166         }
33167         this.startValue = v;
33168         this.field.setValue(v);
33169         if(this.autoSize){
33170             var sz = this.boundEl.getSize();
33171             switch(this.autoSize){
33172                 case "width":
33173                 this.setSize(sz.width,  "");
33174                 break;
33175                 case "height":
33176                 this.setSize("",  sz.height);
33177                 break;
33178                 default:
33179                 this.setSize(sz.width,  sz.height);
33180             }
33181         }
33182         this.el.alignTo(this.boundEl, this.alignment);
33183         this.editing = true;
33184         if(Roo.QuickTips){
33185             Roo.QuickTips.disable();
33186         }
33187         this.show();
33188     },
33189
33190     /**
33191      * Sets the height and width of this editor.
33192      * @param {Number} width The new width
33193      * @param {Number} height The new height
33194      */
33195     setSize : function(w, h){
33196         this.field.setSize(w, h);
33197         if(this.el){
33198             this.el.sync();
33199         }
33200     },
33201
33202     /**
33203      * Realigns the editor to the bound field based on the current alignment config value.
33204      */
33205     realign : function(){
33206         this.el.alignTo(this.boundEl, this.alignment);
33207     },
33208
33209     /**
33210      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33211      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33212      */
33213     completeEdit : function(remainVisible){
33214         if(!this.editing){
33215             return;
33216         }
33217         var v = this.getValue();
33218         if(this.revertInvalid !== false && !this.field.isValid()){
33219             v = this.startValue;
33220             this.cancelEdit(true);
33221         }
33222         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33223             this.editing = false;
33224             this.hide();
33225             return;
33226         }
33227         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33228             this.editing = false;
33229             if(this.updateEl && this.boundEl){
33230                 this.boundEl.update(v);
33231             }
33232             if(remainVisible !== true){
33233                 this.hide();
33234             }
33235             this.fireEvent("complete", this, v, this.startValue);
33236         }
33237     },
33238
33239     // private
33240     onShow : function(){
33241         this.el.show();
33242         if(this.hideEl !== false){
33243             this.boundEl.hide();
33244         }
33245         this.field.show();
33246         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33247             this.fixIEFocus = true;
33248             this.deferredFocus.defer(50, this);
33249         }else{
33250             this.field.focus();
33251         }
33252         this.fireEvent("startedit", this.boundEl, this.startValue);
33253     },
33254
33255     deferredFocus : function(){
33256         if(this.editing){
33257             this.field.focus();
33258         }
33259     },
33260
33261     /**
33262      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33263      * reverted to the original starting value.
33264      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33265      * cancel (defaults to false)
33266      */
33267     cancelEdit : function(remainVisible){
33268         if(this.editing){
33269             this.setValue(this.startValue);
33270             if(remainVisible !== true){
33271                 this.hide();
33272             }
33273         }
33274     },
33275
33276     // private
33277     onBlur : function(){
33278         if(this.allowBlur !== true && this.editing){
33279             this.completeEdit();
33280         }
33281     },
33282
33283     // private
33284     onHide : function(){
33285         if(this.editing){
33286             this.completeEdit();
33287             return;
33288         }
33289         this.field.blur();
33290         if(this.field.collapse){
33291             this.field.collapse();
33292         }
33293         this.el.hide();
33294         if(this.hideEl !== false){
33295             this.boundEl.show();
33296         }
33297         if(Roo.QuickTips){
33298             Roo.QuickTips.enable();
33299         }
33300     },
33301
33302     /**
33303      * Sets the data value of the editor
33304      * @param {Mixed} value Any valid value supported by the underlying field
33305      */
33306     setValue : function(v){
33307         this.field.setValue(v);
33308     },
33309
33310     /**
33311      * Gets the data value of the editor
33312      * @return {Mixed} The data value
33313      */
33314     getValue : function(){
33315         return this.field.getValue();
33316     }
33317 });/*
33318  * Based on:
33319  * Ext JS Library 1.1.1
33320  * Copyright(c) 2006-2007, Ext JS, LLC.
33321  *
33322  * Originally Released Under LGPL - original licence link has changed is not relivant.
33323  *
33324  * Fork - LGPL
33325  * <script type="text/javascript">
33326  */
33327  
33328 /**
33329  * @class Roo.BasicDialog
33330  * @extends Roo.util.Observable
33331  * @parent none builder
33332  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33333  * <pre><code>
33334 var dlg = new Roo.BasicDialog("my-dlg", {
33335     height: 200,
33336     width: 300,
33337     minHeight: 100,
33338     minWidth: 150,
33339     modal: true,
33340     proxyDrag: true,
33341     shadow: true
33342 });
33343 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33344 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33345 dlg.addButton('Cancel', dlg.hide, dlg);
33346 dlg.show();
33347 </code></pre>
33348   <b>A Dialog should always be a direct child of the body element.</b>
33349  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33350  * @cfg {String} title Default text to display in the title bar (defaults to null)
33351  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33352  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33353  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33354  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33355  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33356  * (defaults to null with no animation)
33357  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33358  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33359  * property for valid values (defaults to 'all')
33360  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33361  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33362  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33363  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33364  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33365  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33366  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33367  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33368  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33369  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33370  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33371  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33372  * draggable = true (defaults to false)
33373  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33374  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33375  * shadow (defaults to false)
33376  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33377  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33378  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33379  * @cfg {Array} buttons Array of buttons
33380  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33381  * @constructor
33382  * Create a new BasicDialog.
33383  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33384  * @param {Object} config Configuration options
33385  */
33386 Roo.BasicDialog = function(el, config){
33387     this.el = Roo.get(el);
33388     var dh = Roo.DomHelper;
33389     if(!this.el && config && config.autoCreate){
33390         if(typeof config.autoCreate == "object"){
33391             if(!config.autoCreate.id){
33392                 config.autoCreate.id = el;
33393             }
33394             this.el = dh.append(document.body,
33395                         config.autoCreate, true);
33396         }else{
33397             this.el = dh.append(document.body,
33398                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33399         }
33400     }
33401     el = this.el;
33402     el.setDisplayed(true);
33403     el.hide = this.hideAction;
33404     this.id = el.id;
33405     el.addClass("x-dlg");
33406
33407     Roo.apply(this, config);
33408
33409     this.proxy = el.createProxy("x-dlg-proxy");
33410     this.proxy.hide = this.hideAction;
33411     this.proxy.setOpacity(.5);
33412     this.proxy.hide();
33413
33414     if(config.width){
33415         el.setWidth(config.width);
33416     }
33417     if(config.height){
33418         el.setHeight(config.height);
33419     }
33420     this.size = el.getSize();
33421     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33422         this.xy = [config.x,config.y];
33423     }else{
33424         this.xy = el.getCenterXY(true);
33425     }
33426     /** The header element @type Roo.Element */
33427     this.header = el.child("> .x-dlg-hd");
33428     /** The body element @type Roo.Element */
33429     this.body = el.child("> .x-dlg-bd");
33430     /** The footer element @type Roo.Element */
33431     this.footer = el.child("> .x-dlg-ft");
33432
33433     if(!this.header){
33434         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33435     }
33436     if(!this.body){
33437         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33438     }
33439
33440     this.header.unselectable();
33441     if(this.title){
33442         this.header.update(this.title);
33443     }
33444     // this element allows the dialog to be focused for keyboard event
33445     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33446     this.focusEl.swallowEvent("click", true);
33447
33448     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33449
33450     // wrap the body and footer for special rendering
33451     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33452     if(this.footer){
33453         this.bwrap.dom.appendChild(this.footer.dom);
33454     }
33455
33456     this.bg = this.el.createChild({
33457         tag: "div", cls:"x-dlg-bg",
33458         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33459     });
33460     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33461
33462
33463     if(this.autoScroll !== false && !this.autoTabs){
33464         this.body.setStyle("overflow", "auto");
33465     }
33466
33467     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33468
33469     if(this.closable !== false){
33470         this.el.addClass("x-dlg-closable");
33471         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33472         this.close.on("click", this.closeClick, this);
33473         this.close.addClassOnOver("x-dlg-close-over");
33474     }
33475     if(this.collapsible !== false){
33476         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33477         this.collapseBtn.on("click", this.collapseClick, this);
33478         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33479         this.header.on("dblclick", this.collapseClick, this);
33480     }
33481     if(this.resizable !== false){
33482         this.el.addClass("x-dlg-resizable");
33483         this.resizer = new Roo.Resizable(el, {
33484             minWidth: this.minWidth || 80,
33485             minHeight:this.minHeight || 80,
33486             handles: this.resizeHandles || "all",
33487             pinned: true
33488         });
33489         this.resizer.on("beforeresize", this.beforeResize, this);
33490         this.resizer.on("resize", this.onResize, this);
33491     }
33492     if(this.draggable !== false){
33493         el.addClass("x-dlg-draggable");
33494         if (!this.proxyDrag) {
33495             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33496         }
33497         else {
33498             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33499         }
33500         dd.setHandleElId(this.header.id);
33501         dd.endDrag = this.endMove.createDelegate(this);
33502         dd.startDrag = this.startMove.createDelegate(this);
33503         dd.onDrag = this.onDrag.createDelegate(this);
33504         dd.scroll = false;
33505         this.dd = dd;
33506     }
33507     if(this.modal){
33508         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33509         this.mask.enableDisplayMode("block");
33510         this.mask.hide();
33511         this.el.addClass("x-dlg-modal");
33512     }
33513     if(this.shadow){
33514         this.shadow = new Roo.Shadow({
33515             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33516             offset : this.shadowOffset
33517         });
33518     }else{
33519         this.shadowOffset = 0;
33520     }
33521     if(Roo.useShims && this.shim !== false){
33522         this.shim = this.el.createShim();
33523         this.shim.hide = this.hideAction;
33524         this.shim.hide();
33525     }else{
33526         this.shim = false;
33527     }
33528     if(this.autoTabs){
33529         this.initTabs();
33530     }
33531     if (this.buttons) { 
33532         var bts= this.buttons;
33533         this.buttons = [];
33534         Roo.each(bts, function(b) {
33535             this.addButton(b);
33536         }, this);
33537     }
33538     
33539     
33540     this.addEvents({
33541         /**
33542          * @event keydown
33543          * Fires when a key is pressed
33544          * @param {Roo.BasicDialog} this
33545          * @param {Roo.EventObject} e
33546          */
33547         "keydown" : true,
33548         /**
33549          * @event move
33550          * Fires when this dialog is moved by the user.
33551          * @param {Roo.BasicDialog} this
33552          * @param {Number} x The new page X
33553          * @param {Number} y The new page Y
33554          */
33555         "move" : true,
33556         /**
33557          * @event resize
33558          * Fires when this dialog is resized by the user.
33559          * @param {Roo.BasicDialog} this
33560          * @param {Number} width The new width
33561          * @param {Number} height The new height
33562          */
33563         "resize" : true,
33564         /**
33565          * @event beforehide
33566          * Fires before this dialog is hidden.
33567          * @param {Roo.BasicDialog} this
33568          */
33569         "beforehide" : true,
33570         /**
33571          * @event hide
33572          * Fires when this dialog is hidden.
33573          * @param {Roo.BasicDialog} this
33574          */
33575         "hide" : true,
33576         /**
33577          * @event beforeshow
33578          * Fires before this dialog is shown.
33579          * @param {Roo.BasicDialog} this
33580          */
33581         "beforeshow" : true,
33582         /**
33583          * @event show
33584          * Fires when this dialog is shown.
33585          * @param {Roo.BasicDialog} this
33586          */
33587         "show" : true
33588     });
33589     el.on("keydown", this.onKeyDown, this);
33590     el.on("mousedown", this.toFront, this);
33591     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33592     this.el.hide();
33593     Roo.DialogManager.register(this);
33594     Roo.BasicDialog.superclass.constructor.call(this);
33595 };
33596
33597 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33598     shadowOffset: Roo.isIE ? 6 : 5,
33599     minHeight: 80,
33600     minWidth: 200,
33601     minButtonWidth: 75,
33602     defaultButton: null,
33603     buttonAlign: "right",
33604     tabTag: 'div',
33605     firstShow: true,
33606
33607     /**
33608      * Sets the dialog title text
33609      * @param {String} text The title text to display
33610      * @return {Roo.BasicDialog} this
33611      */
33612     setTitle : function(text){
33613         this.header.update(text);
33614         return this;
33615     },
33616
33617     // private
33618     closeClick : function(){
33619         this.hide();
33620     },
33621
33622     // private
33623     collapseClick : function(){
33624         this[this.collapsed ? "expand" : "collapse"]();
33625     },
33626
33627     /**
33628      * Collapses the dialog to its minimized state (only the title bar is visible).
33629      * Equivalent to the user clicking the collapse dialog button.
33630      */
33631     collapse : function(){
33632         if(!this.collapsed){
33633             this.collapsed = true;
33634             this.el.addClass("x-dlg-collapsed");
33635             this.restoreHeight = this.el.getHeight();
33636             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33637         }
33638     },
33639
33640     /**
33641      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33642      * clicking the expand dialog button.
33643      */
33644     expand : function(){
33645         if(this.collapsed){
33646             this.collapsed = false;
33647             this.el.removeClass("x-dlg-collapsed");
33648             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33649         }
33650     },
33651
33652     /**
33653      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33654      * @return {Roo.TabPanel} The tabs component
33655      */
33656     initTabs : function(){
33657         var tabs = this.getTabs();
33658         while(tabs.getTab(0)){
33659             tabs.removeTab(0);
33660         }
33661         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33662             var dom = el.dom;
33663             tabs.addTab(Roo.id(dom), dom.title);
33664             dom.title = "";
33665         });
33666         tabs.activate(0);
33667         return tabs;
33668     },
33669
33670     // private
33671     beforeResize : function(){
33672         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33673     },
33674
33675     // private
33676     onResize : function(){
33677         this.refreshSize();
33678         this.syncBodyHeight();
33679         this.adjustAssets();
33680         this.focus();
33681         this.fireEvent("resize", this, this.size.width, this.size.height);
33682     },
33683
33684     // private
33685     onKeyDown : function(e){
33686         if(this.isVisible()){
33687             this.fireEvent("keydown", this, e);
33688         }
33689     },
33690
33691     /**
33692      * Resizes the dialog.
33693      * @param {Number} width
33694      * @param {Number} height
33695      * @return {Roo.BasicDialog} this
33696      */
33697     resizeTo : function(width, height){
33698         this.el.setSize(width, height);
33699         this.size = {width: width, height: height};
33700         this.syncBodyHeight();
33701         if(this.fixedcenter){
33702             this.center();
33703         }
33704         if(this.isVisible()){
33705             this.constrainXY();
33706             this.adjustAssets();
33707         }
33708         this.fireEvent("resize", this, width, height);
33709         return this;
33710     },
33711
33712
33713     /**
33714      * Resizes the dialog to fit the specified content size.
33715      * @param {Number} width
33716      * @param {Number} height
33717      * @return {Roo.BasicDialog} this
33718      */
33719     setContentSize : function(w, h){
33720         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33721         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33722         //if(!this.el.isBorderBox()){
33723             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33724             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33725         //}
33726         if(this.tabs){
33727             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33728             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33729         }
33730         this.resizeTo(w, h);
33731         return this;
33732     },
33733
33734     /**
33735      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33736      * executed in response to a particular key being pressed while the dialog is active.
33737      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33738      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33739      * @param {Function} fn The function to call
33740      * @param {Object} scope (optional) The scope of the function
33741      * @return {Roo.BasicDialog} this
33742      */
33743     addKeyListener : function(key, fn, scope){
33744         var keyCode, shift, ctrl, alt;
33745         if(typeof key == "object" && !(key instanceof Array)){
33746             keyCode = key["key"];
33747             shift = key["shift"];
33748             ctrl = key["ctrl"];
33749             alt = key["alt"];
33750         }else{
33751             keyCode = key;
33752         }
33753         var handler = function(dlg, e){
33754             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33755                 var k = e.getKey();
33756                 if(keyCode instanceof Array){
33757                     for(var i = 0, len = keyCode.length; i < len; i++){
33758                         if(keyCode[i] == k){
33759                           fn.call(scope || window, dlg, k, e);
33760                           return;
33761                         }
33762                     }
33763                 }else{
33764                     if(k == keyCode){
33765                         fn.call(scope || window, dlg, k, e);
33766                     }
33767                 }
33768             }
33769         };
33770         this.on("keydown", handler);
33771         return this;
33772     },
33773
33774     /**
33775      * Returns the TabPanel component (creates it if it doesn't exist).
33776      * Note: If you wish to simply check for the existence of tabs without creating them,
33777      * check for a null 'tabs' property.
33778      * @return {Roo.TabPanel} The tabs component
33779      */
33780     getTabs : function(){
33781         if(!this.tabs){
33782             this.el.addClass("x-dlg-auto-tabs");
33783             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33784             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33785         }
33786         return this.tabs;
33787     },
33788
33789     /**
33790      * Adds a button to the footer section of the dialog.
33791      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33792      * object or a valid Roo.DomHelper element config
33793      * @param {Function} handler The function called when the button is clicked
33794      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33795      * @return {Roo.Button} The new button
33796      */
33797     addButton : function(config, handler, scope){
33798         var dh = Roo.DomHelper;
33799         if(!this.footer){
33800             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33801         }
33802         if(!this.btnContainer){
33803             var tb = this.footer.createChild({
33804
33805                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33806                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33807             }, null, true);
33808             this.btnContainer = tb.firstChild.firstChild.firstChild;
33809         }
33810         var bconfig = {
33811             handler: handler,
33812             scope: scope,
33813             minWidth: this.minButtonWidth,
33814             hideParent:true
33815         };
33816         if(typeof config == "string"){
33817             bconfig.text = config;
33818         }else{
33819             if(config.tag){
33820                 bconfig.dhconfig = config;
33821             }else{
33822                 Roo.apply(bconfig, config);
33823             }
33824         }
33825         var fc = false;
33826         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33827             bconfig.position = Math.max(0, bconfig.position);
33828             fc = this.btnContainer.childNodes[bconfig.position];
33829         }
33830          
33831         var btn = new Roo.Button(
33832             fc ? 
33833                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33834                 : this.btnContainer.appendChild(document.createElement("td")),
33835             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33836             bconfig
33837         );
33838         this.syncBodyHeight();
33839         if(!this.buttons){
33840             /**
33841              * Array of all the buttons that have been added to this dialog via addButton
33842              * @type Array
33843              */
33844             this.buttons = [];
33845         }
33846         this.buttons.push(btn);
33847         return btn;
33848     },
33849
33850     /**
33851      * Sets the default button to be focused when the dialog is displayed.
33852      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33853      * @return {Roo.BasicDialog} this
33854      */
33855     setDefaultButton : function(btn){
33856         this.defaultButton = btn;
33857         return this;
33858     },
33859
33860     // private
33861     getHeaderFooterHeight : function(safe){
33862         var height = 0;
33863         if(this.header){
33864            height += this.header.getHeight();
33865         }
33866         if(this.footer){
33867            var fm = this.footer.getMargins();
33868             height += (this.footer.getHeight()+fm.top+fm.bottom);
33869         }
33870         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33871         height += this.centerBg.getPadding("tb");
33872         return height;
33873     },
33874
33875     // private
33876     syncBodyHeight : function()
33877     {
33878         var bd = this.body, // the text
33879             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33880             bw = this.bwrap;
33881         var height = this.size.height - this.getHeaderFooterHeight(false);
33882         bd.setHeight(height-bd.getMargins("tb"));
33883         var hh = this.header.getHeight();
33884         var h = this.size.height-hh;
33885         cb.setHeight(h);
33886         
33887         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33888         bw.setHeight(h-cb.getPadding("tb"));
33889         
33890         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33891         bd.setWidth(bw.getWidth(true));
33892         if(this.tabs){
33893             this.tabs.syncHeight();
33894             if(Roo.isIE){
33895                 this.tabs.el.repaint();
33896             }
33897         }
33898     },
33899
33900     /**
33901      * Restores the previous state of the dialog if Roo.state is configured.
33902      * @return {Roo.BasicDialog} this
33903      */
33904     restoreState : function(){
33905         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33906         if(box && box.width){
33907             this.xy = [box.x, box.y];
33908             this.resizeTo(box.width, box.height);
33909         }
33910         return this;
33911     },
33912
33913     // private
33914     beforeShow : function(){
33915         this.expand();
33916         if(this.fixedcenter){
33917             this.xy = this.el.getCenterXY(true);
33918         }
33919         if(this.modal){
33920             Roo.get(document.body).addClass("x-body-masked");
33921             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33922             this.mask.show();
33923         }
33924         this.constrainXY();
33925     },
33926
33927     // private
33928     animShow : function(){
33929         var b = Roo.get(this.animateTarget).getBox();
33930         this.proxy.setSize(b.width, b.height);
33931         this.proxy.setLocation(b.x, b.y);
33932         this.proxy.show();
33933         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33934                     true, .35, this.showEl.createDelegate(this));
33935     },
33936
33937     /**
33938      * Shows the dialog.
33939      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33940      * @return {Roo.BasicDialog} this
33941      */
33942     show : function(animateTarget){
33943         if (this.fireEvent("beforeshow", this) === false){
33944             return;
33945         }
33946         if(this.syncHeightBeforeShow){
33947             this.syncBodyHeight();
33948         }else if(this.firstShow){
33949             this.firstShow = false;
33950             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33951         }
33952         this.animateTarget = animateTarget || this.animateTarget;
33953         if(!this.el.isVisible()){
33954             this.beforeShow();
33955             if(this.animateTarget && Roo.get(this.animateTarget)){
33956                 this.animShow();
33957             }else{
33958                 this.showEl();
33959             }
33960         }
33961         return this;
33962     },
33963
33964     // private
33965     showEl : function(){
33966         this.proxy.hide();
33967         this.el.setXY(this.xy);
33968         this.el.show();
33969         this.adjustAssets(true);
33970         this.toFront();
33971         this.focus();
33972         // IE peekaboo bug - fix found by Dave Fenwick
33973         if(Roo.isIE){
33974             this.el.repaint();
33975         }
33976         this.fireEvent("show", this);
33977     },
33978
33979     /**
33980      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33981      * dialog itself will receive focus.
33982      */
33983     focus : function(){
33984         if(this.defaultButton){
33985             this.defaultButton.focus();
33986         }else{
33987             this.focusEl.focus();
33988         }
33989     },
33990
33991     // private
33992     constrainXY : function(){
33993         if(this.constraintoviewport !== false){
33994             if(!this.viewSize){
33995                 if(this.container){
33996                     var s = this.container.getSize();
33997                     this.viewSize = [s.width, s.height];
33998                 }else{
33999                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
34000                 }
34001             }
34002             var s = Roo.get(this.container||document).getScroll();
34003
34004             var x = this.xy[0], y = this.xy[1];
34005             var w = this.size.width, h = this.size.height;
34006             var vw = this.viewSize[0], vh = this.viewSize[1];
34007             // only move it if it needs it
34008             var moved = false;
34009             // first validate right/bottom
34010             if(x + w > vw+s.left){
34011                 x = vw - w;
34012                 moved = true;
34013             }
34014             if(y + h > vh+s.top){
34015                 y = vh - h;
34016                 moved = true;
34017             }
34018             // then make sure top/left isn't negative
34019             if(x < s.left){
34020                 x = s.left;
34021                 moved = true;
34022             }
34023             if(y < s.top){
34024                 y = s.top;
34025                 moved = true;
34026             }
34027             if(moved){
34028                 // cache xy
34029                 this.xy = [x, y];
34030                 if(this.isVisible()){
34031                     this.el.setLocation(x, y);
34032                     this.adjustAssets();
34033                 }
34034             }
34035         }
34036     },
34037
34038     // private
34039     onDrag : function(){
34040         if(!this.proxyDrag){
34041             this.xy = this.el.getXY();
34042             this.adjustAssets();
34043         }
34044     },
34045
34046     // private
34047     adjustAssets : function(doShow){
34048         var x = this.xy[0], y = this.xy[1];
34049         var w = this.size.width, h = this.size.height;
34050         if(doShow === true){
34051             if(this.shadow){
34052                 this.shadow.show(this.el);
34053             }
34054             if(this.shim){
34055                 this.shim.show();
34056             }
34057         }
34058         if(this.shadow && this.shadow.isVisible()){
34059             this.shadow.show(this.el);
34060         }
34061         if(this.shim && this.shim.isVisible()){
34062             this.shim.setBounds(x, y, w, h);
34063         }
34064     },
34065
34066     // private
34067     adjustViewport : function(w, h){
34068         if(!w || !h){
34069             w = Roo.lib.Dom.getViewWidth();
34070             h = Roo.lib.Dom.getViewHeight();
34071         }
34072         // cache the size
34073         this.viewSize = [w, h];
34074         if(this.modal && this.mask.isVisible()){
34075             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34076             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34077         }
34078         if(this.isVisible()){
34079             this.constrainXY();
34080         }
34081     },
34082
34083     /**
34084      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34085      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34086      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34087      */
34088     destroy : function(removeEl){
34089         if(this.isVisible()){
34090             this.animateTarget = null;
34091             this.hide();
34092         }
34093         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34094         if(this.tabs){
34095             this.tabs.destroy(removeEl);
34096         }
34097         Roo.destroy(
34098              this.shim,
34099              this.proxy,
34100              this.resizer,
34101              this.close,
34102              this.mask
34103         );
34104         if(this.dd){
34105             this.dd.unreg();
34106         }
34107         if(this.buttons){
34108            for(var i = 0, len = this.buttons.length; i < len; i++){
34109                this.buttons[i].destroy();
34110            }
34111         }
34112         this.el.removeAllListeners();
34113         if(removeEl === true){
34114             this.el.update("");
34115             this.el.remove();
34116         }
34117         Roo.DialogManager.unregister(this);
34118     },
34119
34120     // private
34121     startMove : function(){
34122         if(this.proxyDrag){
34123             this.proxy.show();
34124         }
34125         if(this.constraintoviewport !== false){
34126             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34127         }
34128     },
34129
34130     // private
34131     endMove : function(){
34132         if(!this.proxyDrag){
34133             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34134         }else{
34135             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34136             this.proxy.hide();
34137         }
34138         this.refreshSize();
34139         this.adjustAssets();
34140         this.focus();
34141         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34142     },
34143
34144     /**
34145      * Brings this dialog to the front of any other visible dialogs
34146      * @return {Roo.BasicDialog} this
34147      */
34148     toFront : function(){
34149         Roo.DialogManager.bringToFront(this);
34150         return this;
34151     },
34152
34153     /**
34154      * Sends this dialog to the back (under) of any other visible dialogs
34155      * @return {Roo.BasicDialog} this
34156      */
34157     toBack : function(){
34158         Roo.DialogManager.sendToBack(this);
34159         return this;
34160     },
34161
34162     /**
34163      * Centers this dialog in the viewport
34164      * @return {Roo.BasicDialog} this
34165      */
34166     center : function(){
34167         var xy = this.el.getCenterXY(true);
34168         this.moveTo(xy[0], xy[1]);
34169         return this;
34170     },
34171
34172     /**
34173      * Moves the dialog's top-left corner to the specified point
34174      * @param {Number} x
34175      * @param {Number} y
34176      * @return {Roo.BasicDialog} this
34177      */
34178     moveTo : function(x, y){
34179         this.xy = [x,y];
34180         if(this.isVisible()){
34181             this.el.setXY(this.xy);
34182             this.adjustAssets();
34183         }
34184         return this;
34185     },
34186
34187     /**
34188      * Aligns the dialog to the specified element
34189      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34190      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34191      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34192      * @return {Roo.BasicDialog} this
34193      */
34194     alignTo : function(element, position, offsets){
34195         this.xy = this.el.getAlignToXY(element, position, offsets);
34196         if(this.isVisible()){
34197             this.el.setXY(this.xy);
34198             this.adjustAssets();
34199         }
34200         return this;
34201     },
34202
34203     /**
34204      * Anchors an element to another element and realigns it when the window is resized.
34205      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34206      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
34207      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34208      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34209      * is a number, it is used as the buffer delay (defaults to 50ms).
34210      * @return {Roo.BasicDialog} this
34211      */
34212     anchorTo : function(el, alignment, offsets, monitorScroll){
34213         var action = function(){
34214             this.alignTo(el, alignment, offsets);
34215         };
34216         Roo.EventManager.onWindowResize(action, this);
34217         var tm = typeof monitorScroll;
34218         if(tm != 'undefined'){
34219             Roo.EventManager.on(window, 'scroll', action, this,
34220                 {buffer: tm == 'number' ? monitorScroll : 50});
34221         }
34222         action.call(this);
34223         return this;
34224     },
34225
34226     /**
34227      * Returns true if the dialog is visible
34228      * @return {Boolean}
34229      */
34230     isVisible : function(){
34231         return this.el.isVisible();
34232     },
34233
34234     // private
34235     animHide : function(callback){
34236         var b = Roo.get(this.animateTarget).getBox();
34237         this.proxy.show();
34238         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34239         this.el.hide();
34240         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34241                     this.hideEl.createDelegate(this, [callback]));
34242     },
34243
34244     /**
34245      * Hides the dialog.
34246      * @param {Function} callback (optional) Function to call when the dialog is hidden
34247      * @return {Roo.BasicDialog} this
34248      */
34249     hide : function(callback){
34250         if (this.fireEvent("beforehide", this) === false){
34251             return;
34252         }
34253         if(this.shadow){
34254             this.shadow.hide();
34255         }
34256         if(this.shim) {
34257           this.shim.hide();
34258         }
34259         // sometimes animateTarget seems to get set.. causing problems...
34260         // this just double checks..
34261         if(this.animateTarget && Roo.get(this.animateTarget)) {
34262            this.animHide(callback);
34263         }else{
34264             this.el.hide();
34265             this.hideEl(callback);
34266         }
34267         return this;
34268     },
34269
34270     // private
34271     hideEl : function(callback){
34272         this.proxy.hide();
34273         if(this.modal){
34274             this.mask.hide();
34275             Roo.get(document.body).removeClass("x-body-masked");
34276         }
34277         this.fireEvent("hide", this);
34278         if(typeof callback == "function"){
34279             callback();
34280         }
34281     },
34282
34283     // private
34284     hideAction : function(){
34285         this.setLeft("-10000px");
34286         this.setTop("-10000px");
34287         this.setStyle("visibility", "hidden");
34288     },
34289
34290     // private
34291     refreshSize : function(){
34292         this.size = this.el.getSize();
34293         this.xy = this.el.getXY();
34294         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34295     },
34296
34297     // private
34298     // z-index is managed by the DialogManager and may be overwritten at any time
34299     setZIndex : function(index){
34300         if(this.modal){
34301             this.mask.setStyle("z-index", index);
34302         }
34303         if(this.shim){
34304             this.shim.setStyle("z-index", ++index);
34305         }
34306         if(this.shadow){
34307             this.shadow.setZIndex(++index);
34308         }
34309         this.el.setStyle("z-index", ++index);
34310         if(this.proxy){
34311             this.proxy.setStyle("z-index", ++index);
34312         }
34313         if(this.resizer){
34314             this.resizer.proxy.setStyle("z-index", ++index);
34315         }
34316
34317         this.lastZIndex = index;
34318     },
34319
34320     /**
34321      * Returns the element for this dialog
34322      * @return {Roo.Element} The underlying dialog Element
34323      */
34324     getEl : function(){
34325         return this.el;
34326     }
34327 });
34328
34329 /**
34330  * @class Roo.DialogManager
34331  * Provides global access to BasicDialogs that have been created and
34332  * support for z-indexing (layering) multiple open dialogs.
34333  */
34334 Roo.DialogManager = function(){
34335     var list = {};
34336     var accessList = [];
34337     var front = null;
34338
34339     // private
34340     var sortDialogs = function(d1, d2){
34341         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34342     };
34343
34344     // private
34345     var orderDialogs = function(){
34346         accessList.sort(sortDialogs);
34347         var seed = Roo.DialogManager.zseed;
34348         for(var i = 0, len = accessList.length; i < len; i++){
34349             var dlg = accessList[i];
34350             if(dlg){
34351                 dlg.setZIndex(seed + (i*10));
34352             }
34353         }
34354     };
34355
34356     return {
34357         /**
34358          * The starting z-index for BasicDialogs (defaults to 9000)
34359          * @type Number The z-index value
34360          */
34361         zseed : 9000,
34362
34363         // private
34364         register : function(dlg){
34365             list[dlg.id] = dlg;
34366             accessList.push(dlg);
34367         },
34368
34369         // private
34370         unregister : function(dlg){
34371             delete list[dlg.id];
34372             var i=0;
34373             var len=0;
34374             if(!accessList.indexOf){
34375                 for(  i = 0, len = accessList.length; i < len; i++){
34376                     if(accessList[i] == dlg){
34377                         accessList.splice(i, 1);
34378                         return;
34379                     }
34380                 }
34381             }else{
34382                  i = accessList.indexOf(dlg);
34383                 if(i != -1){
34384                     accessList.splice(i, 1);
34385                 }
34386             }
34387         },
34388
34389         /**
34390          * Gets a registered dialog by id
34391          * @param {String/Object} id The id of the dialog or a dialog
34392          * @return {Roo.BasicDialog} this
34393          */
34394         get : function(id){
34395             return typeof id == "object" ? id : list[id];
34396         },
34397
34398         /**
34399          * Brings the specified dialog to the front
34400          * @param {String/Object} dlg The id of the dialog or a dialog
34401          * @return {Roo.BasicDialog} this
34402          */
34403         bringToFront : function(dlg){
34404             dlg = this.get(dlg);
34405             if(dlg != front){
34406                 front = dlg;
34407                 dlg._lastAccess = new Date().getTime();
34408                 orderDialogs();
34409             }
34410             return dlg;
34411         },
34412
34413         /**
34414          * Sends the specified dialog to the back
34415          * @param {String/Object} dlg The id of the dialog or a dialog
34416          * @return {Roo.BasicDialog} this
34417          */
34418         sendToBack : function(dlg){
34419             dlg = this.get(dlg);
34420             dlg._lastAccess = -(new Date().getTime());
34421             orderDialogs();
34422             return dlg;
34423         },
34424
34425         /**
34426          * Hides all dialogs
34427          */
34428         hideAll : function(){
34429             for(var id in list){
34430                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34431                     list[id].hide();
34432                 }
34433             }
34434         }
34435     };
34436 }();
34437
34438 /**
34439  * @class Roo.LayoutDialog
34440  * @extends Roo.BasicDialog
34441  * @children Roo.ContentPanel
34442  * @parent builder none
34443  * Dialog which provides adjustments for working with a layout in a Dialog.
34444  * Add your necessary layout config options to the dialog's config.<br>
34445  * Example usage (including a nested layout):
34446  * <pre><code>
34447 if(!dialog){
34448     dialog = new Roo.LayoutDialog("download-dlg", {
34449         modal: true,
34450         width:600,
34451         height:450,
34452         shadow:true,
34453         minWidth:500,
34454         minHeight:350,
34455         autoTabs:true,
34456         proxyDrag:true,
34457         // layout config merges with the dialog config
34458         center:{
34459             tabPosition: "top",
34460             alwaysShowTabs: true
34461         }
34462     });
34463     dialog.addKeyListener(27, dialog.hide, dialog);
34464     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34465     dialog.addButton("Build It!", this.getDownload, this);
34466
34467     // we can even add nested layouts
34468     var innerLayout = new Roo.BorderLayout("dl-inner", {
34469         east: {
34470             initialSize: 200,
34471             autoScroll:true,
34472             split:true
34473         },
34474         center: {
34475             autoScroll:true
34476         }
34477     });
34478     innerLayout.beginUpdate();
34479     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34480     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34481     innerLayout.endUpdate(true);
34482
34483     var layout = dialog.getLayout();
34484     layout.beginUpdate();
34485     layout.add("center", new Roo.ContentPanel("standard-panel",
34486                         {title: "Download the Source", fitToFrame:true}));
34487     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34488                {title: "Build your own roo.js"}));
34489     layout.getRegion("center").showPanel(sp);
34490     layout.endUpdate();
34491 }
34492 </code></pre>
34493     * @constructor
34494     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34495     * @param {Object} config configuration options
34496   */
34497 Roo.LayoutDialog = function(el, cfg){
34498     
34499     var config=  cfg;
34500     if (typeof(cfg) == 'undefined') {
34501         config = Roo.apply({}, el);
34502         // not sure why we use documentElement here.. - it should always be body.
34503         // IE7 borks horribly if we use documentElement.
34504         // webkit also does not like documentElement - it creates a body element...
34505         el = Roo.get( document.body || document.documentElement ).createChild();
34506         //config.autoCreate = true;
34507     }
34508     
34509     
34510     config.autoTabs = false;
34511     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34512     this.body.setStyle({overflow:"hidden", position:"relative"});
34513     this.layout = new Roo.BorderLayout(this.body.dom, config);
34514     this.layout.monitorWindowResize = false;
34515     this.el.addClass("x-dlg-auto-layout");
34516     // fix case when center region overwrites center function
34517     this.center = Roo.BasicDialog.prototype.center;
34518     this.on("show", this.layout.layout, this.layout, true);
34519     if (config.items) {
34520         var xitems = config.items;
34521         delete config.items;
34522         Roo.each(xitems, this.addxtype, this);
34523     }
34524     
34525     
34526 };
34527 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34528     
34529     
34530     /**
34531      * @cfg {Roo.LayoutRegion} east  
34532      */
34533     /**
34534      * @cfg {Roo.LayoutRegion} west
34535      */
34536     /**
34537      * @cfg {Roo.LayoutRegion} south
34538      */
34539     /**
34540      * @cfg {Roo.LayoutRegion} north
34541      */
34542     /**
34543      * @cfg {Roo.LayoutRegion} center
34544      */
34545     /**
34546      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34547      */
34548     
34549     
34550     /**
34551      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34552      * @deprecated
34553      */
34554     endUpdate : function(){
34555         this.layout.endUpdate();
34556     },
34557
34558     /**
34559      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34560      *  @deprecated
34561      */
34562     beginUpdate : function(){
34563         this.layout.beginUpdate();
34564     },
34565
34566     /**
34567      * Get the BorderLayout for this dialog
34568      * @return {Roo.BorderLayout}
34569      */
34570     getLayout : function(){
34571         return this.layout;
34572     },
34573
34574     showEl : function(){
34575         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34576         if(Roo.isIE7){
34577             this.layout.layout();
34578         }
34579     },
34580
34581     // private
34582     // Use the syncHeightBeforeShow config option to control this automatically
34583     syncBodyHeight : function(){
34584         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34585         if(this.layout){this.layout.layout();}
34586     },
34587     
34588       /**
34589      * Add an xtype element (actually adds to the layout.)
34590      * @return {Object} xdata xtype object data.
34591      */
34592     
34593     addxtype : function(c) {
34594         return this.layout.addxtype(c);
34595     }
34596 });/*
34597  * Based on:
34598  * Ext JS Library 1.1.1
34599  * Copyright(c) 2006-2007, Ext JS, LLC.
34600  *
34601  * Originally Released Under LGPL - original licence link has changed is not relivant.
34602  *
34603  * Fork - LGPL
34604  * <script type="text/javascript">
34605  */
34606  
34607 /**
34608  * @class Roo.MessageBox
34609  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34610  * Example usage:
34611  *<pre><code>
34612 // Basic alert:
34613 Roo.Msg.alert('Status', 'Changes saved successfully.');
34614
34615 // Prompt for user data:
34616 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34617     if (btn == 'ok'){
34618         // process text value...
34619     }
34620 });
34621
34622 // Show a dialog using config options:
34623 Roo.Msg.show({
34624    title:'Save Changes?',
34625    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34626    buttons: Roo.Msg.YESNOCANCEL,
34627    fn: processResult,
34628    animEl: 'elId'
34629 });
34630 </code></pre>
34631  * @static
34632  */
34633 Roo.MessageBox = function(){
34634     var dlg, opt, mask, waitTimer;
34635     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34636     var buttons, activeTextEl, bwidth;
34637
34638     // private
34639     var handleButton = function(button){
34640         dlg.hide();
34641         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34642     };
34643
34644     // private
34645     var handleHide = function(){
34646         if(opt && opt.cls){
34647             dlg.el.removeClass(opt.cls);
34648         }
34649         if(waitTimer){
34650             Roo.TaskMgr.stop(waitTimer);
34651             waitTimer = null;
34652         }
34653     };
34654
34655     // private
34656     var updateButtons = function(b){
34657         var width = 0;
34658         if(!b){
34659             buttons["ok"].hide();
34660             buttons["cancel"].hide();
34661             buttons["yes"].hide();
34662             buttons["no"].hide();
34663             dlg.footer.dom.style.display = 'none';
34664             return width;
34665         }
34666         dlg.footer.dom.style.display = '';
34667         for(var k in buttons){
34668             if(typeof buttons[k] != "function"){
34669                 if(b[k]){
34670                     buttons[k].show();
34671                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34672                     width += buttons[k].el.getWidth()+15;
34673                 }else{
34674                     buttons[k].hide();
34675                 }
34676             }
34677         }
34678         return width;
34679     };
34680
34681     // private
34682     var handleEsc = function(d, k, e){
34683         if(opt && opt.closable !== false){
34684             dlg.hide();
34685         }
34686         if(e){
34687             e.stopEvent();
34688         }
34689     };
34690
34691     return {
34692         /**
34693          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34694          * @return {Roo.BasicDialog} The BasicDialog element
34695          */
34696         getDialog : function(){
34697            if(!dlg){
34698                 dlg = new Roo.BasicDialog("x-msg-box", {
34699                     autoCreate : true,
34700                     shadow: true,
34701                     draggable: true,
34702                     resizable:false,
34703                     constraintoviewport:false,
34704                     fixedcenter:true,
34705                     collapsible : false,
34706                     shim:true,
34707                     modal: true,
34708                     width:400, height:100,
34709                     buttonAlign:"center",
34710                     closeClick : function(){
34711                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34712                             handleButton("no");
34713                         }else{
34714                             handleButton("cancel");
34715                         }
34716                     }
34717                 });
34718                 dlg.on("hide", handleHide);
34719                 mask = dlg.mask;
34720                 dlg.addKeyListener(27, handleEsc);
34721                 buttons = {};
34722                 var bt = this.buttonText;
34723                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34724                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34725                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34726                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34727                 bodyEl = dlg.body.createChild({
34728
34729                     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>'
34730                 });
34731                 msgEl = bodyEl.dom.firstChild;
34732                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34733                 textboxEl.enableDisplayMode();
34734                 textboxEl.addKeyListener([10,13], function(){
34735                     if(dlg.isVisible() && opt && opt.buttons){
34736                         if(opt.buttons.ok){
34737                             handleButton("ok");
34738                         }else if(opt.buttons.yes){
34739                             handleButton("yes");
34740                         }
34741                     }
34742                 });
34743                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34744                 textareaEl.enableDisplayMode();
34745                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34746                 progressEl.enableDisplayMode();
34747                 var pf = progressEl.dom.firstChild;
34748                 if (pf) {
34749                     pp = Roo.get(pf.firstChild);
34750                     pp.setHeight(pf.offsetHeight);
34751                 }
34752                 
34753             }
34754             return dlg;
34755         },
34756
34757         /**
34758          * Updates the message box body text
34759          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34760          * the XHTML-compliant non-breaking space character '&amp;#160;')
34761          * @return {Roo.MessageBox} This message box
34762          */
34763         updateText : function(text){
34764             if(!dlg.isVisible() && !opt.width){
34765                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34766             }
34767             msgEl.innerHTML = text || '&#160;';
34768       
34769             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34770             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34771             var w = Math.max(
34772                     Math.min(opt.width || cw , this.maxWidth), 
34773                     Math.max(opt.minWidth || this.minWidth, bwidth)
34774             );
34775             if(opt.prompt){
34776                 activeTextEl.setWidth(w);
34777             }
34778             if(dlg.isVisible()){
34779                 dlg.fixedcenter = false;
34780             }
34781             // to big, make it scroll. = But as usual stupid IE does not support
34782             // !important..
34783             
34784             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34785                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34786                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34787             } else {
34788                 bodyEl.dom.style.height = '';
34789                 bodyEl.dom.style.overflowY = '';
34790             }
34791             if (cw > w) {
34792                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34793             } else {
34794                 bodyEl.dom.style.overflowX = '';
34795             }
34796             
34797             dlg.setContentSize(w, bodyEl.getHeight());
34798             if(dlg.isVisible()){
34799                 dlg.fixedcenter = true;
34800             }
34801             return this;
34802         },
34803
34804         /**
34805          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34806          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34807          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34808          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34809          * @return {Roo.MessageBox} This message box
34810          */
34811         updateProgress : function(value, text){
34812             if(text){
34813                 this.updateText(text);
34814             }
34815             if (pp) { // weird bug on my firefox - for some reason this is not defined
34816                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34817             }
34818             return this;
34819         },        
34820
34821         /**
34822          * Returns true if the message box is currently displayed
34823          * @return {Boolean} True if the message box is visible, else false
34824          */
34825         isVisible : function(){
34826             return dlg && dlg.isVisible();  
34827         },
34828
34829         /**
34830          * Hides the message box if it is displayed
34831          */
34832         hide : function(){
34833             if(this.isVisible()){
34834                 dlg.hide();
34835             }  
34836         },
34837
34838         /**
34839          * Displays a new message box, or reinitializes an existing message box, based on the config options
34840          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34841          * The following config object properties are supported:
34842          * <pre>
34843 Property    Type             Description
34844 ----------  ---------------  ------------------------------------------------------------------------------------
34845 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34846                                    closes (defaults to undefined)
34847 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34848                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34849 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34850                                    progress and wait dialogs will ignore this property and always hide the
34851                                    close button as they can only be closed programmatically.
34852 cls               String           A custom CSS class to apply to the message box element
34853 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34854                                    displayed (defaults to 75)
34855 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34856                                    function will be btn (the name of the button that was clicked, if applicable,
34857                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34858                                    Progress and wait dialogs will ignore this option since they do not respond to
34859                                    user actions and can only be closed programmatically, so any required function
34860                                    should be called by the same code after it closes the dialog.
34861 icon              String           A CSS class that provides a background image to be used as an icon for
34862                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34863 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34864 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34865 modal             Boolean          False to allow user interaction with the page while the message box is
34866                                    displayed (defaults to true)
34867 msg               String           A string that will replace the existing message box body text (defaults
34868                                    to the XHTML-compliant non-breaking space character '&#160;')
34869 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34870 progress          Boolean          True to display a progress bar (defaults to false)
34871 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34872 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34873 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34874 title             String           The title text
34875 value             String           The string value to set into the active textbox element if displayed
34876 wait              Boolean          True to display a progress bar (defaults to false)
34877 width             Number           The width of the dialog in pixels
34878 </pre>
34879          *
34880          * Example usage:
34881          * <pre><code>
34882 Roo.Msg.show({
34883    title: 'Address',
34884    msg: 'Please enter your address:',
34885    width: 300,
34886    buttons: Roo.MessageBox.OKCANCEL,
34887    multiline: true,
34888    fn: saveAddress,
34889    animEl: 'addAddressBtn'
34890 });
34891 </code></pre>
34892          * @param {Object} config Configuration options
34893          * @return {Roo.MessageBox} This message box
34894          */
34895         show : function(options)
34896         {
34897             
34898             // this causes nightmares if you show one dialog after another
34899             // especially on callbacks..
34900              
34901             if(this.isVisible()){
34902                 
34903                 this.hide();
34904                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34905                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34906                 Roo.log("New Dialog Message:" +  options.msg )
34907                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34908                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34909                 
34910             }
34911             var d = this.getDialog();
34912             opt = options;
34913             d.setTitle(opt.title || "&#160;");
34914             d.close.setDisplayed(opt.closable !== false);
34915             activeTextEl = textboxEl;
34916             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34917             if(opt.prompt){
34918                 if(opt.multiline){
34919                     textboxEl.hide();
34920                     textareaEl.show();
34921                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34922                         opt.multiline : this.defaultTextHeight);
34923                     activeTextEl = textareaEl;
34924                 }else{
34925                     textboxEl.show();
34926                     textareaEl.hide();
34927                 }
34928             }else{
34929                 textboxEl.hide();
34930                 textareaEl.hide();
34931             }
34932             progressEl.setDisplayed(opt.progress === true);
34933             this.updateProgress(0);
34934             activeTextEl.dom.value = opt.value || "";
34935             if(opt.prompt){
34936                 dlg.setDefaultButton(activeTextEl);
34937             }else{
34938                 var bs = opt.buttons;
34939                 var db = null;
34940                 if(bs && bs.ok){
34941                     db = buttons["ok"];
34942                 }else if(bs && bs.yes){
34943                     db = buttons["yes"];
34944                 }
34945                 dlg.setDefaultButton(db);
34946             }
34947             bwidth = updateButtons(opt.buttons);
34948             this.updateText(opt.msg);
34949             if(opt.cls){
34950                 d.el.addClass(opt.cls);
34951             }
34952             d.proxyDrag = opt.proxyDrag === true;
34953             d.modal = opt.modal !== false;
34954             d.mask = opt.modal !== false ? mask : false;
34955             if(!d.isVisible()){
34956                 // force it to the end of the z-index stack so it gets a cursor in FF
34957                 document.body.appendChild(dlg.el.dom);
34958                 d.animateTarget = null;
34959                 d.show(options.animEl);
34960             }
34961             return this;
34962         },
34963
34964         /**
34965          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34966          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34967          * and closing the message box when the process is complete.
34968          * @param {String} title The title bar text
34969          * @param {String} msg The message box body text
34970          * @return {Roo.MessageBox} This message box
34971          */
34972         progress : function(title, msg){
34973             this.show({
34974                 title : title,
34975                 msg : msg,
34976                 buttons: false,
34977                 progress:true,
34978                 closable:false,
34979                 minWidth: this.minProgressWidth,
34980                 modal : true
34981             });
34982             return this;
34983         },
34984
34985         /**
34986          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34987          * If a callback function is passed it will be called after the user clicks the button, and the
34988          * id of the button that was clicked will be passed as the only parameter to the callback
34989          * (could also be the top-right close button).
34990          * @param {String} title The title bar text
34991          * @param {String} msg The message box body text
34992          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34993          * @param {Object} scope (optional) The scope of the callback function
34994          * @return {Roo.MessageBox} This message box
34995          */
34996         alert : function(title, msg, fn, scope){
34997             this.show({
34998                 title : title,
34999                 msg : msg,
35000                 buttons: this.OK,
35001                 fn: fn,
35002                 scope : scope,
35003                 modal : true
35004             });
35005             return this;
35006         },
35007
35008         /**
35009          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
35010          * interaction while waiting for a long-running process to complete that does not have defined intervals.
35011          * You are responsible for closing the message box when the process is complete.
35012          * @param {String} msg The message box body text
35013          * @param {String} title (optional) The title bar text
35014          * @return {Roo.MessageBox} This message box
35015          */
35016         wait : function(msg, title){
35017             this.show({
35018                 title : title,
35019                 msg : msg,
35020                 buttons: false,
35021                 closable:false,
35022                 progress:true,
35023                 modal:true,
35024                 width:300,
35025                 wait:true
35026             });
35027             waitTimer = Roo.TaskMgr.start({
35028                 run: function(i){
35029                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35030                 },
35031                 interval: 1000
35032             });
35033             return this;
35034         },
35035
35036         /**
35037          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35038          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35039          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35040          * @param {String} title The title bar text
35041          * @param {String} msg The message box body text
35042          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35043          * @param {Object} scope (optional) The scope of the callback function
35044          * @return {Roo.MessageBox} This message box
35045          */
35046         confirm : function(title, msg, fn, scope){
35047             this.show({
35048                 title : title,
35049                 msg : msg,
35050                 buttons: this.YESNO,
35051                 fn: fn,
35052                 scope : scope,
35053                 modal : true
35054             });
35055             return this;
35056         },
35057
35058         /**
35059          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35060          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35061          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35062          * (could also be the top-right close button) and the text that was entered will be passed as the two
35063          * parameters to the callback.
35064          * @param {String} title The title bar text
35065          * @param {String} msg The message box body text
35066          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35067          * @param {Object} scope (optional) The scope of the callback function
35068          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35069          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35070          * @return {Roo.MessageBox} This message box
35071          */
35072         prompt : function(title, msg, fn, scope, multiline){
35073             this.show({
35074                 title : title,
35075                 msg : msg,
35076                 buttons: this.OKCANCEL,
35077                 fn: fn,
35078                 minWidth:250,
35079                 scope : scope,
35080                 prompt:true,
35081                 multiline: multiline,
35082                 modal : true
35083             });
35084             return this;
35085         },
35086
35087         /**
35088          * Button config that displays a single OK button
35089          * @type Object
35090          */
35091         OK : {ok:true},
35092         /**
35093          * Button config that displays Yes and No buttons
35094          * @type Object
35095          */
35096         YESNO : {yes:true, no:true},
35097         /**
35098          * Button config that displays OK and Cancel buttons
35099          * @type Object
35100          */
35101         OKCANCEL : {ok:true, cancel:true},
35102         /**
35103          * Button config that displays Yes, No and Cancel buttons
35104          * @type Object
35105          */
35106         YESNOCANCEL : {yes:true, no:true, cancel:true},
35107
35108         /**
35109          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35110          * @type Number
35111          */
35112         defaultTextHeight : 75,
35113         /**
35114          * The maximum width in pixels of the message box (defaults to 600)
35115          * @type Number
35116          */
35117         maxWidth : 600,
35118         /**
35119          * The minimum width in pixels of the message box (defaults to 100)
35120          * @type Number
35121          */
35122         minWidth : 100,
35123         /**
35124          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35125          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35126          * @type Number
35127          */
35128         minProgressWidth : 250,
35129         /**
35130          * An object containing the default button text strings that can be overriden for localized language support.
35131          * Supported properties are: ok, cancel, yes and no.
35132          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35133          * @type Object
35134          */
35135         buttonText : {
35136             ok : "OK",
35137             cancel : "Cancel",
35138             yes : "Yes",
35139             no : "No"
35140         }
35141     };
35142 }();
35143
35144 /**
35145  * Shorthand for {@link Roo.MessageBox}
35146  */
35147 Roo.Msg = Roo.MessageBox;/*
35148  * Based on:
35149  * Ext JS Library 1.1.1
35150  * Copyright(c) 2006-2007, Ext JS, LLC.
35151  *
35152  * Originally Released Under LGPL - original licence link has changed is not relivant.
35153  *
35154  * Fork - LGPL
35155  * <script type="text/javascript">
35156  */
35157 /**
35158  * @class Roo.QuickTips
35159  * Provides attractive and customizable tooltips for any element.
35160  * @static
35161  */
35162 Roo.QuickTips = function(){
35163     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35164     var ce, bd, xy, dd;
35165     var visible = false, disabled = true, inited = false;
35166     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35167     
35168     var onOver = function(e){
35169         if(disabled){
35170             return;
35171         }
35172         var t = e.getTarget();
35173         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35174             return;
35175         }
35176         if(ce && t == ce.el){
35177             clearTimeout(hideProc);
35178             return;
35179         }
35180         if(t && tagEls[t.id]){
35181             tagEls[t.id].el = t;
35182             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35183             return;
35184         }
35185         var ttp, et = Roo.fly(t);
35186         var ns = cfg.namespace;
35187         if(tm.interceptTitles && t.title){
35188             ttp = t.title;
35189             t.qtip = ttp;
35190             t.removeAttribute("title");
35191             e.preventDefault();
35192         }else{
35193             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35194         }
35195         if(ttp){
35196             showProc = show.defer(tm.showDelay, tm, [{
35197                 el: t, 
35198                 text: ttp.replace(/\\n/g,'<br/>'),
35199                 width: et.getAttributeNS(ns, cfg.width),
35200                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35201                 title: et.getAttributeNS(ns, cfg.title),
35202                     cls: et.getAttributeNS(ns, cfg.cls)
35203             }]);
35204         }
35205     };
35206     
35207     var onOut = function(e){
35208         clearTimeout(showProc);
35209         var t = e.getTarget();
35210         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35211             hideProc = setTimeout(hide, tm.hideDelay);
35212         }
35213     };
35214     
35215     var onMove = function(e){
35216         if(disabled){
35217             return;
35218         }
35219         xy = e.getXY();
35220         xy[1] += 18;
35221         if(tm.trackMouse && ce){
35222             el.setXY(xy);
35223         }
35224     };
35225     
35226     var onDown = function(e){
35227         clearTimeout(showProc);
35228         clearTimeout(hideProc);
35229         if(!e.within(el)){
35230             if(tm.hideOnClick){
35231                 hide();
35232                 tm.disable();
35233                 tm.enable.defer(100, tm);
35234             }
35235         }
35236     };
35237     
35238     var getPad = function(){
35239         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35240     };
35241
35242     var show = function(o){
35243         if(disabled){
35244             return;
35245         }
35246         clearTimeout(dismissProc);
35247         ce = o;
35248         if(removeCls){ // in case manually hidden
35249             el.removeClass(removeCls);
35250             removeCls = null;
35251         }
35252         if(ce.cls){
35253             el.addClass(ce.cls);
35254             removeCls = ce.cls;
35255         }
35256         if(ce.title){
35257             tipTitle.update(ce.title);
35258             tipTitle.show();
35259         }else{
35260             tipTitle.update('');
35261             tipTitle.hide();
35262         }
35263         el.dom.style.width  = tm.maxWidth+'px';
35264         //tipBody.dom.style.width = '';
35265         tipBodyText.update(o.text);
35266         var p = getPad(), w = ce.width;
35267         if(!w){
35268             var td = tipBodyText.dom;
35269             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35270             if(aw > tm.maxWidth){
35271                 w = tm.maxWidth;
35272             }else if(aw < tm.minWidth){
35273                 w = tm.minWidth;
35274             }else{
35275                 w = aw;
35276             }
35277         }
35278         //tipBody.setWidth(w);
35279         el.setWidth(parseInt(w, 10) + p);
35280         if(ce.autoHide === false){
35281             close.setDisplayed(true);
35282             if(dd){
35283                 dd.unlock();
35284             }
35285         }else{
35286             close.setDisplayed(false);
35287             if(dd){
35288                 dd.lock();
35289             }
35290         }
35291         if(xy){
35292             el.avoidY = xy[1]-18;
35293             el.setXY(xy);
35294         }
35295         if(tm.animate){
35296             el.setOpacity(.1);
35297             el.setStyle("visibility", "visible");
35298             el.fadeIn({callback: afterShow});
35299         }else{
35300             afterShow();
35301         }
35302     };
35303     
35304     var afterShow = function(){
35305         if(ce){
35306             el.show();
35307             esc.enable();
35308             if(tm.autoDismiss && ce.autoHide !== false){
35309                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35310             }
35311         }
35312     };
35313     
35314     var hide = function(noanim){
35315         clearTimeout(dismissProc);
35316         clearTimeout(hideProc);
35317         ce = null;
35318         if(el.isVisible()){
35319             esc.disable();
35320             if(noanim !== true && tm.animate){
35321                 el.fadeOut({callback: afterHide});
35322             }else{
35323                 afterHide();
35324             } 
35325         }
35326     };
35327     
35328     var afterHide = function(){
35329         el.hide();
35330         if(removeCls){
35331             el.removeClass(removeCls);
35332             removeCls = null;
35333         }
35334     };
35335     
35336     return {
35337         /**
35338         * @cfg {Number} minWidth
35339         * The minimum width of the quick tip (defaults to 40)
35340         */
35341        minWidth : 40,
35342         /**
35343         * @cfg {Number} maxWidth
35344         * The maximum width of the quick tip (defaults to 300)
35345         */
35346        maxWidth : 300,
35347         /**
35348         * @cfg {Boolean} interceptTitles
35349         * True to automatically use the element's DOM title value if available (defaults to false)
35350         */
35351        interceptTitles : false,
35352         /**
35353         * @cfg {Boolean} trackMouse
35354         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35355         */
35356        trackMouse : false,
35357         /**
35358         * @cfg {Boolean} hideOnClick
35359         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35360         */
35361        hideOnClick : true,
35362         /**
35363         * @cfg {Number} showDelay
35364         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35365         */
35366        showDelay : 500,
35367         /**
35368         * @cfg {Number} hideDelay
35369         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35370         */
35371        hideDelay : 200,
35372         /**
35373         * @cfg {Boolean} autoHide
35374         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35375         * Used in conjunction with hideDelay.
35376         */
35377        autoHide : true,
35378         /**
35379         * @cfg {Boolean}
35380         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35381         * (defaults to true).  Used in conjunction with autoDismissDelay.
35382         */
35383        autoDismiss : true,
35384         /**
35385         * @cfg {Number}
35386         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35387         */
35388        autoDismissDelay : 5000,
35389        /**
35390         * @cfg {Boolean} animate
35391         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35392         */
35393        animate : false,
35394
35395        /**
35396         * @cfg {String} title
35397         * Title text to display (defaults to '').  This can be any valid HTML markup.
35398         */
35399         title: '',
35400        /**
35401         * @cfg {String} text
35402         * Body text to display (defaults to '').  This can be any valid HTML markup.
35403         */
35404         text : '',
35405        /**
35406         * @cfg {String} cls
35407         * A CSS class to apply to the base quick tip element (defaults to '').
35408         */
35409         cls : '',
35410        /**
35411         * @cfg {Number} width
35412         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35413         * minWidth or maxWidth.
35414         */
35415         width : null,
35416
35417     /**
35418      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35419      * or display QuickTips in a page.
35420      */
35421        init : function(){
35422           tm = Roo.QuickTips;
35423           cfg = tm.tagConfig;
35424           if(!inited){
35425               if(!Roo.isReady){ // allow calling of init() before onReady
35426                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35427                   return;
35428               }
35429               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35430               el.fxDefaults = {stopFx: true};
35431               // maximum custom styling
35432               //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>');
35433               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>');              
35434               tipTitle = el.child('h3');
35435               tipTitle.enableDisplayMode("block");
35436               tipBody = el.child('div.x-tip-bd');
35437               tipBodyText = el.child('div.x-tip-bd-inner');
35438               //bdLeft = el.child('div.x-tip-bd-left');
35439               //bdRight = el.child('div.x-tip-bd-right');
35440               close = el.child('div.x-tip-close');
35441               close.enableDisplayMode("block");
35442               close.on("click", hide);
35443               var d = Roo.get(document);
35444               d.on("mousedown", onDown);
35445               d.on("mouseover", onOver);
35446               d.on("mouseout", onOut);
35447               d.on("mousemove", onMove);
35448               esc = d.addKeyListener(27, hide);
35449               esc.disable();
35450               if(Roo.dd.DD){
35451                   dd = el.initDD("default", null, {
35452                       onDrag : function(){
35453                           el.sync();  
35454                       }
35455                   });
35456                   dd.setHandleElId(tipTitle.id);
35457                   dd.lock();
35458               }
35459               inited = true;
35460           }
35461           this.enable(); 
35462        },
35463
35464     /**
35465      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35466      * are supported:
35467      * <pre>
35468 Property    Type                   Description
35469 ----------  ---------------------  ------------------------------------------------------------------------
35470 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35471      * </ul>
35472      * @param {Object} config The config object
35473      */
35474        register : function(config){
35475            var cs = config instanceof Array ? config : arguments;
35476            for(var i = 0, len = cs.length; i < len; i++) {
35477                var c = cs[i];
35478                var target = c.target;
35479                if(target){
35480                    if(target instanceof Array){
35481                        for(var j = 0, jlen = target.length; j < jlen; j++){
35482                            tagEls[target[j]] = c;
35483                        }
35484                    }else{
35485                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35486                    }
35487                }
35488            }
35489        },
35490
35491     /**
35492      * Removes this quick tip from its element and destroys it.
35493      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35494      */
35495        unregister : function(el){
35496            delete tagEls[Roo.id(el)];
35497        },
35498
35499     /**
35500      * Enable this quick tip.
35501      */
35502        enable : function(){
35503            if(inited && disabled){
35504                locks.pop();
35505                if(locks.length < 1){
35506                    disabled = false;
35507                }
35508            }
35509        },
35510
35511     /**
35512      * Disable this quick tip.
35513      */
35514        disable : function(){
35515           disabled = true;
35516           clearTimeout(showProc);
35517           clearTimeout(hideProc);
35518           clearTimeout(dismissProc);
35519           if(ce){
35520               hide(true);
35521           }
35522           locks.push(1);
35523        },
35524
35525     /**
35526      * Returns true if the quick tip is enabled, else false.
35527      */
35528        isEnabled : function(){
35529             return !disabled;
35530        },
35531
35532         // private
35533        tagConfig : {
35534            namespace : "roo", // was ext?? this may break..
35535            alt_namespace : "ext",
35536            attribute : "qtip",
35537            width : "width",
35538            target : "target",
35539            title : "qtitle",
35540            hide : "hide",
35541            cls : "qclass"
35542        }
35543    };
35544 }();
35545
35546 // backwards compat
35547 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35548  * Based on:
35549  * Ext JS Library 1.1.1
35550  * Copyright(c) 2006-2007, Ext JS, LLC.
35551  *
35552  * Originally Released Under LGPL - original licence link has changed is not relivant.
35553  *
35554  * Fork - LGPL
35555  * <script type="text/javascript">
35556  */
35557  
35558
35559 /**
35560  * @class Roo.tree.TreePanel
35561  * @extends Roo.data.Tree
35562  * @cfg {Roo.tree.TreeNode} root The root node
35563  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35564  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35565  * @cfg {Boolean} enableDD true to enable drag and drop
35566  * @cfg {Boolean} enableDrag true to enable just drag
35567  * @cfg {Boolean} enableDrop true to enable just drop
35568  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35569  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35570  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35571  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35572  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35573  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35574  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35575  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35576  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35577  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35578  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35579  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35580  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35581  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35582  * @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>
35583  * @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>
35584  * 
35585  * @constructor
35586  * @param {String/HTMLElement/Element} el The container element
35587  * @param {Object} config
35588  */
35589 Roo.tree.TreePanel = function(el, config){
35590     var root = false;
35591     var loader = false;
35592     if (config.root) {
35593         root = config.root;
35594         delete config.root;
35595     }
35596     if (config.loader) {
35597         loader = config.loader;
35598         delete config.loader;
35599     }
35600     
35601     Roo.apply(this, config);
35602     Roo.tree.TreePanel.superclass.constructor.call(this);
35603     this.el = Roo.get(el);
35604     this.el.addClass('x-tree');
35605     //console.log(root);
35606     if (root) {
35607         this.setRootNode( Roo.factory(root, Roo.tree));
35608     }
35609     if (loader) {
35610         this.loader = Roo.factory(loader, Roo.tree);
35611     }
35612    /**
35613     * Read-only. The id of the container element becomes this TreePanel's id.
35614     */
35615     this.id = this.el.id;
35616     this.addEvents({
35617         /**
35618         * @event beforeload
35619         * Fires before a node is loaded, return false to cancel
35620         * @param {Node} node The node being loaded
35621         */
35622         "beforeload" : true,
35623         /**
35624         * @event load
35625         * Fires when a node is loaded
35626         * @param {Node} node The node that was loaded
35627         */
35628         "load" : true,
35629         /**
35630         * @event textchange
35631         * Fires when the text for a node is changed
35632         * @param {Node} node The node
35633         * @param {String} text The new text
35634         * @param {String} oldText The old text
35635         */
35636         "textchange" : true,
35637         /**
35638         * @event beforeexpand
35639         * Fires before a node is expanded, return false to cancel.
35640         * @param {Node} node The node
35641         * @param {Boolean} deep
35642         * @param {Boolean} anim
35643         */
35644         "beforeexpand" : true,
35645         /**
35646         * @event beforecollapse
35647         * Fires before a node is collapsed, return false to cancel.
35648         * @param {Node} node The node
35649         * @param {Boolean} deep
35650         * @param {Boolean} anim
35651         */
35652         "beforecollapse" : true,
35653         /**
35654         * @event expand
35655         * Fires when a node is expanded
35656         * @param {Node} node The node
35657         */
35658         "expand" : true,
35659         /**
35660         * @event disabledchange
35661         * Fires when the disabled status of a node changes
35662         * @param {Node} node The node
35663         * @param {Boolean} disabled
35664         */
35665         "disabledchange" : true,
35666         /**
35667         * @event collapse
35668         * Fires when a node is collapsed
35669         * @param {Node} node The node
35670         */
35671         "collapse" : true,
35672         /**
35673         * @event beforeclick
35674         * Fires before click processing on a node. Return false to cancel the default action.
35675         * @param {Node} node The node
35676         * @param {Roo.EventObject} e The event object
35677         */
35678         "beforeclick":true,
35679         /**
35680         * @event checkchange
35681         * Fires when a node with a checkbox's checked property changes
35682         * @param {Node} this This node
35683         * @param {Boolean} checked
35684         */
35685         "checkchange":true,
35686         /**
35687         * @event click
35688         * Fires when a node is clicked
35689         * @param {Node} node The node
35690         * @param {Roo.EventObject} e The event object
35691         */
35692         "click":true,
35693         /**
35694         * @event dblclick
35695         * Fires when a node is double clicked
35696         * @param {Node} node The node
35697         * @param {Roo.EventObject} e The event object
35698         */
35699         "dblclick":true,
35700         /**
35701         * @event contextmenu
35702         * Fires when a node is right clicked
35703         * @param {Node} node The node
35704         * @param {Roo.EventObject} e The event object
35705         */
35706         "contextmenu":true,
35707         /**
35708         * @event beforechildrenrendered
35709         * Fires right before the child nodes for a node are rendered
35710         * @param {Node} node The node
35711         */
35712         "beforechildrenrendered":true,
35713         /**
35714         * @event startdrag
35715         * Fires when a node starts being dragged
35716         * @param {Roo.tree.TreePanel} this
35717         * @param {Roo.tree.TreeNode} node
35718         * @param {event} e The raw browser event
35719         */ 
35720        "startdrag" : true,
35721        /**
35722         * @event enddrag
35723         * Fires when a drag operation is complete
35724         * @param {Roo.tree.TreePanel} this
35725         * @param {Roo.tree.TreeNode} node
35726         * @param {event} e The raw browser event
35727         */
35728        "enddrag" : true,
35729        /**
35730         * @event dragdrop
35731         * Fires when a dragged node is dropped on a valid DD target
35732         * @param {Roo.tree.TreePanel} this
35733         * @param {Roo.tree.TreeNode} node
35734         * @param {DD} dd The dd it was dropped on
35735         * @param {event} e The raw browser event
35736         */
35737        "dragdrop" : true,
35738        /**
35739         * @event beforenodedrop
35740         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35741         * passed to handlers has the following properties:<br />
35742         * <ul style="padding:5px;padding-left:16px;">
35743         * <li>tree - The TreePanel</li>
35744         * <li>target - The node being targeted for the drop</li>
35745         * <li>data - The drag data from the drag source</li>
35746         * <li>point - The point of the drop - append, above or below</li>
35747         * <li>source - The drag source</li>
35748         * <li>rawEvent - Raw mouse event</li>
35749         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35750         * to be inserted by setting them on this object.</li>
35751         * <li>cancel - Set this to true to cancel the drop.</li>
35752         * </ul>
35753         * @param {Object} dropEvent
35754         */
35755        "beforenodedrop" : true,
35756        /**
35757         * @event nodedrop
35758         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35759         * passed to handlers has the following properties:<br />
35760         * <ul style="padding:5px;padding-left:16px;">
35761         * <li>tree - The TreePanel</li>
35762         * <li>target - The node being targeted for the drop</li>
35763         * <li>data - The drag data from the drag source</li>
35764         * <li>point - The point of the drop - append, above or below</li>
35765         * <li>source - The drag source</li>
35766         * <li>rawEvent - Raw mouse event</li>
35767         * <li>dropNode - Dropped node(s).</li>
35768         * </ul>
35769         * @param {Object} dropEvent
35770         */
35771        "nodedrop" : true,
35772         /**
35773         * @event nodedragover
35774         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35775         * passed to handlers has the following properties:<br />
35776         * <ul style="padding:5px;padding-left:16px;">
35777         * <li>tree - The TreePanel</li>
35778         * <li>target - The node being targeted for the drop</li>
35779         * <li>data - The drag data from the drag source</li>
35780         * <li>point - The point of the drop - append, above or below</li>
35781         * <li>source - The drag source</li>
35782         * <li>rawEvent - Raw mouse event</li>
35783         * <li>dropNode - Drop node(s) provided by the source.</li>
35784         * <li>cancel - Set this to true to signal drop not allowed.</li>
35785         * </ul>
35786         * @param {Object} dragOverEvent
35787         */
35788        "nodedragover" : true,
35789        /**
35790         * @event appendnode
35791         * Fires when append node to the tree
35792         * @param {Roo.tree.TreePanel} this
35793         * @param {Roo.tree.TreeNode} node
35794         * @param {Number} index The index of the newly appended node
35795         */
35796        "appendnode" : true
35797         
35798     });
35799     if(this.singleExpand){
35800        this.on("beforeexpand", this.restrictExpand, this);
35801     }
35802     if (this.editor) {
35803         this.editor.tree = this;
35804         this.editor = Roo.factory(this.editor, Roo.tree);
35805     }
35806     
35807     if (this.selModel) {
35808         this.selModel = Roo.factory(this.selModel, Roo.tree);
35809     }
35810    
35811 };
35812 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35813     rootVisible : true,
35814     animate: Roo.enableFx,
35815     lines : true,
35816     enableDD : false,
35817     hlDrop : Roo.enableFx,
35818   
35819     renderer: false,
35820     
35821     rendererTip: false,
35822     // private
35823     restrictExpand : function(node){
35824         var p = node.parentNode;
35825         if(p){
35826             if(p.expandedChild && p.expandedChild.parentNode == p){
35827                 p.expandedChild.collapse();
35828             }
35829             p.expandedChild = node;
35830         }
35831     },
35832
35833     // private override
35834     setRootNode : function(node){
35835         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35836         if(!this.rootVisible){
35837             node.ui = new Roo.tree.RootTreeNodeUI(node);
35838         }
35839         return node;
35840     },
35841
35842     /**
35843      * Returns the container element for this TreePanel
35844      */
35845     getEl : function(){
35846         return this.el;
35847     },
35848
35849     /**
35850      * Returns the default TreeLoader for this TreePanel
35851      */
35852     getLoader : function(){
35853         return this.loader;
35854     },
35855
35856     /**
35857      * Expand all nodes
35858      */
35859     expandAll : function(){
35860         this.root.expand(true);
35861     },
35862
35863     /**
35864      * Collapse all nodes
35865      */
35866     collapseAll : function(){
35867         this.root.collapse(true);
35868     },
35869
35870     /**
35871      * Returns the selection model used by this TreePanel
35872      */
35873     getSelectionModel : function(){
35874         if(!this.selModel){
35875             this.selModel = new Roo.tree.DefaultSelectionModel();
35876         }
35877         return this.selModel;
35878     },
35879
35880     /**
35881      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35882      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35883      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35884      * @return {Array}
35885      */
35886     getChecked : function(a, startNode){
35887         startNode = startNode || this.root;
35888         var r = [];
35889         var f = function(){
35890             if(this.attributes.checked){
35891                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35892             }
35893         }
35894         startNode.cascade(f);
35895         return r;
35896     },
35897
35898     /**
35899      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35900      * @param {String} path
35901      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35902      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35903      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35904      */
35905     expandPath : function(path, attr, callback){
35906         attr = attr || "id";
35907         var keys = path.split(this.pathSeparator);
35908         var curNode = this.root;
35909         if(curNode.attributes[attr] != keys[1]){ // invalid root
35910             if(callback){
35911                 callback(false, null);
35912             }
35913             return;
35914         }
35915         var index = 1;
35916         var f = function(){
35917             if(++index == keys.length){
35918                 if(callback){
35919                     callback(true, curNode);
35920                 }
35921                 return;
35922             }
35923             var c = curNode.findChild(attr, keys[index]);
35924             if(!c){
35925                 if(callback){
35926                     callback(false, curNode);
35927                 }
35928                 return;
35929             }
35930             curNode = c;
35931             c.expand(false, false, f);
35932         };
35933         curNode.expand(false, false, f);
35934     },
35935
35936     /**
35937      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35938      * @param {String} path
35939      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35940      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35941      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35942      */
35943     selectPath : function(path, attr, callback){
35944         attr = attr || "id";
35945         var keys = path.split(this.pathSeparator);
35946         var v = keys.pop();
35947         if(keys.length > 0){
35948             var f = function(success, node){
35949                 if(success && node){
35950                     var n = node.findChild(attr, v);
35951                     if(n){
35952                         n.select();
35953                         if(callback){
35954                             callback(true, n);
35955                         }
35956                     }else if(callback){
35957                         callback(false, n);
35958                     }
35959                 }else{
35960                     if(callback){
35961                         callback(false, n);
35962                     }
35963                 }
35964             };
35965             this.expandPath(keys.join(this.pathSeparator), attr, f);
35966         }else{
35967             this.root.select();
35968             if(callback){
35969                 callback(true, this.root);
35970             }
35971         }
35972     },
35973
35974     getTreeEl : function(){
35975         return this.el;
35976     },
35977
35978     /**
35979      * Trigger rendering of this TreePanel
35980      */
35981     render : function(){
35982         if (this.innerCt) {
35983             return this; // stop it rendering more than once!!
35984         }
35985         
35986         this.innerCt = this.el.createChild({tag:"ul",
35987                cls:"x-tree-root-ct " +
35988                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35989
35990         if(this.containerScroll){
35991             Roo.dd.ScrollManager.register(this.el);
35992         }
35993         if((this.enableDD || this.enableDrop) && !this.dropZone){
35994            /**
35995             * The dropZone used by this tree if drop is enabled
35996             * @type Roo.tree.TreeDropZone
35997             */
35998              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35999                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
36000            });
36001         }
36002         if((this.enableDD || this.enableDrag) && !this.dragZone){
36003            /**
36004             * The dragZone used by this tree if drag is enabled
36005             * @type Roo.tree.TreeDragZone
36006             */
36007             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
36008                ddGroup: this.ddGroup || "TreeDD",
36009                scroll: this.ddScroll
36010            });
36011         }
36012         this.getSelectionModel().init(this);
36013         if (!this.root) {
36014             Roo.log("ROOT not set in tree");
36015             return this;
36016         }
36017         this.root.render();
36018         if(!this.rootVisible){
36019             this.root.renderChildren();
36020         }
36021         return this;
36022     }
36023 });/*
36024  * Based on:
36025  * Ext JS Library 1.1.1
36026  * Copyright(c) 2006-2007, Ext JS, LLC.
36027  *
36028  * Originally Released Under LGPL - original licence link has changed is not relivant.
36029  *
36030  * Fork - LGPL
36031  * <script type="text/javascript">
36032  */
36033  
36034
36035 /**
36036  * @class Roo.tree.DefaultSelectionModel
36037  * @extends Roo.util.Observable
36038  * The default single selection for a TreePanel.
36039  * @param {Object} cfg Configuration
36040  */
36041 Roo.tree.DefaultSelectionModel = function(cfg){
36042    this.selNode = null;
36043    
36044    
36045    
36046    this.addEvents({
36047        /**
36048         * @event selectionchange
36049         * Fires when the selected node changes
36050         * @param {DefaultSelectionModel} this
36051         * @param {TreeNode} node the new selection
36052         */
36053        "selectionchange" : true,
36054
36055        /**
36056         * @event beforeselect
36057         * Fires before the selected node changes, return false to cancel the change
36058         * @param {DefaultSelectionModel} this
36059         * @param {TreeNode} node the new selection
36060         * @param {TreeNode} node the old selection
36061         */
36062        "beforeselect" : true
36063    });
36064    
36065     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36066 };
36067
36068 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36069     init : function(tree){
36070         this.tree = tree;
36071         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36072         tree.on("click", this.onNodeClick, this);
36073     },
36074     
36075     onNodeClick : function(node, e){
36076         if (e.ctrlKey && this.selNode == node)  {
36077             this.unselect(node);
36078             return;
36079         }
36080         this.select(node);
36081     },
36082     
36083     /**
36084      * Select a node.
36085      * @param {TreeNode} node The node to select
36086      * @return {TreeNode} The selected node
36087      */
36088     select : function(node){
36089         var last = this.selNode;
36090         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36091             if(last){
36092                 last.ui.onSelectedChange(false);
36093             }
36094             this.selNode = node;
36095             node.ui.onSelectedChange(true);
36096             this.fireEvent("selectionchange", this, node, last);
36097         }
36098         return node;
36099     },
36100     
36101     /**
36102      * Deselect a node.
36103      * @param {TreeNode} node The node to unselect
36104      */
36105     unselect : function(node){
36106         if(this.selNode == node){
36107             this.clearSelections();
36108         }    
36109     },
36110     
36111     /**
36112      * Clear all selections
36113      */
36114     clearSelections : function(){
36115         var n = this.selNode;
36116         if(n){
36117             n.ui.onSelectedChange(false);
36118             this.selNode = null;
36119             this.fireEvent("selectionchange", this, null);
36120         }
36121         return n;
36122     },
36123     
36124     /**
36125      * Get the selected node
36126      * @return {TreeNode} The selected node
36127      */
36128     getSelectedNode : function(){
36129         return this.selNode;    
36130     },
36131     
36132     /**
36133      * Returns true if the node is selected
36134      * @param {TreeNode} node The node to check
36135      * @return {Boolean}
36136      */
36137     isSelected : function(node){
36138         return this.selNode == node;  
36139     },
36140
36141     /**
36142      * Selects the node above the selected node in the tree, intelligently walking the nodes
36143      * @return TreeNode The new selection
36144      */
36145     selectPrevious : function(){
36146         var s = this.selNode || this.lastSelNode;
36147         if(!s){
36148             return null;
36149         }
36150         var ps = s.previousSibling;
36151         if(ps){
36152             if(!ps.isExpanded() || ps.childNodes.length < 1){
36153                 return this.select(ps);
36154             } else{
36155                 var lc = ps.lastChild;
36156                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36157                     lc = lc.lastChild;
36158                 }
36159                 return this.select(lc);
36160             }
36161         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36162             return this.select(s.parentNode);
36163         }
36164         return null;
36165     },
36166
36167     /**
36168      * Selects the node above the selected node in the tree, intelligently walking the nodes
36169      * @return TreeNode The new selection
36170      */
36171     selectNext : function(){
36172         var s = this.selNode || this.lastSelNode;
36173         if(!s){
36174             return null;
36175         }
36176         if(s.firstChild && s.isExpanded()){
36177              return this.select(s.firstChild);
36178          }else if(s.nextSibling){
36179              return this.select(s.nextSibling);
36180          }else if(s.parentNode){
36181             var newS = null;
36182             s.parentNode.bubble(function(){
36183                 if(this.nextSibling){
36184                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36185                     return false;
36186                 }
36187             });
36188             return newS;
36189          }
36190         return null;
36191     },
36192
36193     onKeyDown : function(e){
36194         var s = this.selNode || this.lastSelNode;
36195         // undesirable, but required
36196         var sm = this;
36197         if(!s){
36198             return;
36199         }
36200         var k = e.getKey();
36201         switch(k){
36202              case e.DOWN:
36203                  e.stopEvent();
36204                  this.selectNext();
36205              break;
36206              case e.UP:
36207                  e.stopEvent();
36208                  this.selectPrevious();
36209              break;
36210              case e.RIGHT:
36211                  e.preventDefault();
36212                  if(s.hasChildNodes()){
36213                      if(!s.isExpanded()){
36214                          s.expand();
36215                      }else if(s.firstChild){
36216                          this.select(s.firstChild, e);
36217                      }
36218                  }
36219              break;
36220              case e.LEFT:
36221                  e.preventDefault();
36222                  if(s.hasChildNodes() && s.isExpanded()){
36223                      s.collapse();
36224                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36225                      this.select(s.parentNode, e);
36226                  }
36227              break;
36228         };
36229     }
36230 });
36231
36232 /**
36233  * @class Roo.tree.MultiSelectionModel
36234  * @extends Roo.util.Observable
36235  * Multi selection for a TreePanel.
36236  * @param {Object} cfg Configuration
36237  */
36238 Roo.tree.MultiSelectionModel = function(){
36239    this.selNodes = [];
36240    this.selMap = {};
36241    this.addEvents({
36242        /**
36243         * @event selectionchange
36244         * Fires when the selected nodes change
36245         * @param {MultiSelectionModel} this
36246         * @param {Array} nodes Array of the selected nodes
36247         */
36248        "selectionchange" : true
36249    });
36250    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36251    
36252 };
36253
36254 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36255     init : function(tree){
36256         this.tree = tree;
36257         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36258         tree.on("click", this.onNodeClick, this);
36259     },
36260     
36261     onNodeClick : function(node, e){
36262         this.select(node, e, e.ctrlKey);
36263     },
36264     
36265     /**
36266      * Select a node.
36267      * @param {TreeNode} node The node to select
36268      * @param {EventObject} e (optional) An event associated with the selection
36269      * @param {Boolean} keepExisting True to retain existing selections
36270      * @return {TreeNode} The selected node
36271      */
36272     select : function(node, e, keepExisting){
36273         if(keepExisting !== true){
36274             this.clearSelections(true);
36275         }
36276         if(this.isSelected(node)){
36277             this.lastSelNode = node;
36278             return node;
36279         }
36280         this.selNodes.push(node);
36281         this.selMap[node.id] = node;
36282         this.lastSelNode = node;
36283         node.ui.onSelectedChange(true);
36284         this.fireEvent("selectionchange", this, this.selNodes);
36285         return node;
36286     },
36287     
36288     /**
36289      * Deselect a node.
36290      * @param {TreeNode} node The node to unselect
36291      */
36292     unselect : function(node){
36293         if(this.selMap[node.id]){
36294             node.ui.onSelectedChange(false);
36295             var sn = this.selNodes;
36296             var index = -1;
36297             if(sn.indexOf){
36298                 index = sn.indexOf(node);
36299             }else{
36300                 for(var i = 0, len = sn.length; i < len; i++){
36301                     if(sn[i] == node){
36302                         index = i;
36303                         break;
36304                     }
36305                 }
36306             }
36307             if(index != -1){
36308                 this.selNodes.splice(index, 1);
36309             }
36310             delete this.selMap[node.id];
36311             this.fireEvent("selectionchange", this, this.selNodes);
36312         }
36313     },
36314     
36315     /**
36316      * Clear all selections
36317      */
36318     clearSelections : function(suppressEvent){
36319         var sn = this.selNodes;
36320         if(sn.length > 0){
36321             for(var i = 0, len = sn.length; i < len; i++){
36322                 sn[i].ui.onSelectedChange(false);
36323             }
36324             this.selNodes = [];
36325             this.selMap = {};
36326             if(suppressEvent !== true){
36327                 this.fireEvent("selectionchange", this, this.selNodes);
36328             }
36329         }
36330     },
36331     
36332     /**
36333      * Returns true if the node is selected
36334      * @param {TreeNode} node The node to check
36335      * @return {Boolean}
36336      */
36337     isSelected : function(node){
36338         return this.selMap[node.id] ? true : false;  
36339     },
36340     
36341     /**
36342      * Returns an array of the selected nodes
36343      * @return {Array}
36344      */
36345     getSelectedNodes : function(){
36346         return this.selNodes;    
36347     },
36348
36349     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36350
36351     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36352
36353     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36354 });/*
36355  * Based on:
36356  * Ext JS Library 1.1.1
36357  * Copyright(c) 2006-2007, Ext JS, LLC.
36358  *
36359  * Originally Released Under LGPL - original licence link has changed is not relivant.
36360  *
36361  * Fork - LGPL
36362  * <script type="text/javascript">
36363  */
36364  
36365 /**
36366  * @class Roo.tree.TreeNode
36367  * @extends Roo.data.Node
36368  * @cfg {String} text The text for this node
36369  * @cfg {Boolean} expanded true to start the node expanded
36370  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36371  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36372  * @cfg {Boolean} disabled true to start the node disabled
36373  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36374  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36375  * @cfg {String} cls A css class to be added to the node
36376  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36377  * @cfg {String} href URL of the link used for the node (defaults to #)
36378  * @cfg {String} hrefTarget target frame for the link
36379  * @cfg {String} qtip An Ext QuickTip for the node
36380  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36381  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36382  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36383  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36384  * (defaults to undefined with no checkbox rendered)
36385  * @constructor
36386  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36387  */
36388 Roo.tree.TreeNode = function(attributes){
36389     attributes = attributes || {};
36390     if(typeof attributes == "string"){
36391         attributes = {text: attributes};
36392     }
36393     this.childrenRendered = false;
36394     this.rendered = false;
36395     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36396     this.expanded = attributes.expanded === true;
36397     this.isTarget = attributes.isTarget !== false;
36398     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36399     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36400
36401     /**
36402      * Read-only. The text for this node. To change it use setText().
36403      * @type String
36404      */
36405     this.text = attributes.text;
36406     /**
36407      * True if this node is disabled.
36408      * @type Boolean
36409      */
36410     this.disabled = attributes.disabled === true;
36411
36412     this.addEvents({
36413         /**
36414         * @event textchange
36415         * Fires when the text for this node is changed
36416         * @param {Node} this This node
36417         * @param {String} text The new text
36418         * @param {String} oldText The old text
36419         */
36420         "textchange" : true,
36421         /**
36422         * @event beforeexpand
36423         * Fires before this node is expanded, return false to cancel.
36424         * @param {Node} this This node
36425         * @param {Boolean} deep
36426         * @param {Boolean} anim
36427         */
36428         "beforeexpand" : true,
36429         /**
36430         * @event beforecollapse
36431         * Fires before this node is collapsed, return false to cancel.
36432         * @param {Node} this This node
36433         * @param {Boolean} deep
36434         * @param {Boolean} anim
36435         */
36436         "beforecollapse" : true,
36437         /**
36438         * @event expand
36439         * Fires when this node is expanded
36440         * @param {Node} this This node
36441         */
36442         "expand" : true,
36443         /**
36444         * @event disabledchange
36445         * Fires when the disabled status of this node changes
36446         * @param {Node} this This node
36447         * @param {Boolean} disabled
36448         */
36449         "disabledchange" : true,
36450         /**
36451         * @event collapse
36452         * Fires when this node is collapsed
36453         * @param {Node} this This node
36454         */
36455         "collapse" : true,
36456         /**
36457         * @event beforeclick
36458         * Fires before click processing. Return false to cancel the default action.
36459         * @param {Node} this This node
36460         * @param {Roo.EventObject} e The event object
36461         */
36462         "beforeclick":true,
36463         /**
36464         * @event checkchange
36465         * Fires when a node with a checkbox's checked property changes
36466         * @param {Node} this This node
36467         * @param {Boolean} checked
36468         */
36469         "checkchange":true,
36470         /**
36471         * @event click
36472         * Fires when this node is clicked
36473         * @param {Node} this This node
36474         * @param {Roo.EventObject} e The event object
36475         */
36476         "click":true,
36477         /**
36478         * @event dblclick
36479         * Fires when this node is double clicked
36480         * @param {Node} this This node
36481         * @param {Roo.EventObject} e The event object
36482         */
36483         "dblclick":true,
36484         /**
36485         * @event contextmenu
36486         * Fires when this node is right clicked
36487         * @param {Node} this This node
36488         * @param {Roo.EventObject} e The event object
36489         */
36490         "contextmenu":true,
36491         /**
36492         * @event beforechildrenrendered
36493         * Fires right before the child nodes for this node are rendered
36494         * @param {Node} this This node
36495         */
36496         "beforechildrenrendered":true
36497     });
36498
36499     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36500
36501     /**
36502      * Read-only. The UI for this node
36503      * @type TreeNodeUI
36504      */
36505     this.ui = new uiClass(this);
36506     
36507     // finally support items[]
36508     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36509         return;
36510     }
36511     
36512     
36513     Roo.each(this.attributes.items, function(c) {
36514         this.appendChild(Roo.factory(c,Roo.Tree));
36515     }, this);
36516     delete this.attributes.items;
36517     
36518     
36519     
36520 };
36521 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36522     preventHScroll: true,
36523     /**
36524      * Returns true if this node is expanded
36525      * @return {Boolean}
36526      */
36527     isExpanded : function(){
36528         return this.expanded;
36529     },
36530
36531     /**
36532      * Returns the UI object for this node
36533      * @return {TreeNodeUI}
36534      */
36535     getUI : function(){
36536         return this.ui;
36537     },
36538
36539     // private override
36540     setFirstChild : function(node){
36541         var of = this.firstChild;
36542         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36543         if(this.childrenRendered && of && node != of){
36544             of.renderIndent(true, true);
36545         }
36546         if(this.rendered){
36547             this.renderIndent(true, true);
36548         }
36549     },
36550
36551     // private override
36552     setLastChild : function(node){
36553         var ol = this.lastChild;
36554         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36555         if(this.childrenRendered && ol && node != ol){
36556             ol.renderIndent(true, true);
36557         }
36558         if(this.rendered){
36559             this.renderIndent(true, true);
36560         }
36561     },
36562
36563     // these methods are overridden to provide lazy rendering support
36564     // private override
36565     appendChild : function()
36566     {
36567         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36568         if(node && this.childrenRendered){
36569             node.render();
36570         }
36571         this.ui.updateExpandIcon();
36572         return node;
36573     },
36574
36575     // private override
36576     removeChild : function(node){
36577         this.ownerTree.getSelectionModel().unselect(node);
36578         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36579         // if it's been rendered remove dom node
36580         if(this.childrenRendered){
36581             node.ui.remove();
36582         }
36583         if(this.childNodes.length < 1){
36584             this.collapse(false, false);
36585         }else{
36586             this.ui.updateExpandIcon();
36587         }
36588         if(!this.firstChild) {
36589             this.childrenRendered = false;
36590         }
36591         return node;
36592     },
36593
36594     // private override
36595     insertBefore : function(node, refNode){
36596         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36597         if(newNode && refNode && this.childrenRendered){
36598             node.render();
36599         }
36600         this.ui.updateExpandIcon();
36601         return newNode;
36602     },
36603
36604     /**
36605      * Sets the text for this node
36606      * @param {String} text
36607      */
36608     setText : function(text){
36609         var oldText = this.text;
36610         this.text = text;
36611         this.attributes.text = text;
36612         if(this.rendered){ // event without subscribing
36613             this.ui.onTextChange(this, text, oldText);
36614         }
36615         this.fireEvent("textchange", this, text, oldText);
36616     },
36617
36618     /**
36619      * Triggers selection of this node
36620      */
36621     select : function(){
36622         this.getOwnerTree().getSelectionModel().select(this);
36623     },
36624
36625     /**
36626      * Triggers deselection of this node
36627      */
36628     unselect : function(){
36629         this.getOwnerTree().getSelectionModel().unselect(this);
36630     },
36631
36632     /**
36633      * Returns true if this node is selected
36634      * @return {Boolean}
36635      */
36636     isSelected : function(){
36637         return this.getOwnerTree().getSelectionModel().isSelected(this);
36638     },
36639
36640     /**
36641      * Expand this node.
36642      * @param {Boolean} deep (optional) True to expand all children as well
36643      * @param {Boolean} anim (optional) false to cancel the default animation
36644      * @param {Function} callback (optional) A callback to be called when
36645      * expanding this node completes (does not wait for deep expand to complete).
36646      * Called with 1 parameter, this node.
36647      */
36648     expand : function(deep, anim, callback){
36649         if(!this.expanded){
36650             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36651                 return;
36652             }
36653             if(!this.childrenRendered){
36654                 this.renderChildren();
36655             }
36656             this.expanded = true;
36657             
36658             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36659                 this.ui.animExpand(function(){
36660                     this.fireEvent("expand", this);
36661                     if(typeof callback == "function"){
36662                         callback(this);
36663                     }
36664                     if(deep === true){
36665                         this.expandChildNodes(true);
36666                     }
36667                 }.createDelegate(this));
36668                 return;
36669             }else{
36670                 this.ui.expand();
36671                 this.fireEvent("expand", this);
36672                 if(typeof callback == "function"){
36673                     callback(this);
36674                 }
36675             }
36676         }else{
36677            if(typeof callback == "function"){
36678                callback(this);
36679            }
36680         }
36681         if(deep === true){
36682             this.expandChildNodes(true);
36683         }
36684     },
36685
36686     isHiddenRoot : function(){
36687         return this.isRoot && !this.getOwnerTree().rootVisible;
36688     },
36689
36690     /**
36691      * Collapse this node.
36692      * @param {Boolean} deep (optional) True to collapse all children as well
36693      * @param {Boolean} anim (optional) false to cancel the default animation
36694      */
36695     collapse : function(deep, anim){
36696         if(this.expanded && !this.isHiddenRoot()){
36697             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36698                 return;
36699             }
36700             this.expanded = false;
36701             if((this.getOwnerTree().animate && anim !== false) || anim){
36702                 this.ui.animCollapse(function(){
36703                     this.fireEvent("collapse", this);
36704                     if(deep === true){
36705                         this.collapseChildNodes(true);
36706                     }
36707                 }.createDelegate(this));
36708                 return;
36709             }else{
36710                 this.ui.collapse();
36711                 this.fireEvent("collapse", this);
36712             }
36713         }
36714         if(deep === true){
36715             var cs = this.childNodes;
36716             for(var i = 0, len = cs.length; i < len; i++) {
36717                 cs[i].collapse(true, false);
36718             }
36719         }
36720     },
36721
36722     // private
36723     delayedExpand : function(delay){
36724         if(!this.expandProcId){
36725             this.expandProcId = this.expand.defer(delay, this);
36726         }
36727     },
36728
36729     // private
36730     cancelExpand : function(){
36731         if(this.expandProcId){
36732             clearTimeout(this.expandProcId);
36733         }
36734         this.expandProcId = false;
36735     },
36736
36737     /**
36738      * Toggles expanded/collapsed state of the node
36739      */
36740     toggle : function(){
36741         if(this.expanded){
36742             this.collapse();
36743         }else{
36744             this.expand();
36745         }
36746     },
36747
36748     /**
36749      * Ensures all parent nodes are expanded
36750      */
36751     ensureVisible : function(callback){
36752         var tree = this.getOwnerTree();
36753         tree.expandPath(this.parentNode.getPath(), false, function(){
36754             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36755             Roo.callback(callback);
36756         }.createDelegate(this));
36757     },
36758
36759     /**
36760      * Expand all child nodes
36761      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36762      */
36763     expandChildNodes : function(deep){
36764         var cs = this.childNodes;
36765         for(var i = 0, len = cs.length; i < len; i++) {
36766                 cs[i].expand(deep);
36767         }
36768     },
36769
36770     /**
36771      * Collapse all child nodes
36772      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36773      */
36774     collapseChildNodes : function(deep){
36775         var cs = this.childNodes;
36776         for(var i = 0, len = cs.length; i < len; i++) {
36777                 cs[i].collapse(deep);
36778         }
36779     },
36780
36781     /**
36782      * Disables this node
36783      */
36784     disable : function(){
36785         this.disabled = true;
36786         this.unselect();
36787         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36788             this.ui.onDisableChange(this, true);
36789         }
36790         this.fireEvent("disabledchange", this, true);
36791     },
36792
36793     /**
36794      * Enables this node
36795      */
36796     enable : function(){
36797         this.disabled = false;
36798         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36799             this.ui.onDisableChange(this, false);
36800         }
36801         this.fireEvent("disabledchange", this, false);
36802     },
36803
36804     // private
36805     renderChildren : function(suppressEvent){
36806         if(suppressEvent !== false){
36807             this.fireEvent("beforechildrenrendered", this);
36808         }
36809         var cs = this.childNodes;
36810         for(var i = 0, len = cs.length; i < len; i++){
36811             cs[i].render(true);
36812         }
36813         this.childrenRendered = true;
36814     },
36815
36816     // private
36817     sort : function(fn, scope){
36818         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36819         if(this.childrenRendered){
36820             var cs = this.childNodes;
36821             for(var i = 0, len = cs.length; i < len; i++){
36822                 cs[i].render(true);
36823             }
36824         }
36825     },
36826
36827     // private
36828     render : function(bulkRender){
36829         this.ui.render(bulkRender);
36830         if(!this.rendered){
36831             this.rendered = true;
36832             if(this.expanded){
36833                 this.expanded = false;
36834                 this.expand(false, false);
36835             }
36836         }
36837     },
36838
36839     // private
36840     renderIndent : function(deep, refresh){
36841         if(refresh){
36842             this.ui.childIndent = null;
36843         }
36844         this.ui.renderIndent();
36845         if(deep === true && this.childrenRendered){
36846             var cs = this.childNodes;
36847             for(var i = 0, len = cs.length; i < len; i++){
36848                 cs[i].renderIndent(true, refresh);
36849             }
36850         }
36851     }
36852 });/*
36853  * Based on:
36854  * Ext JS Library 1.1.1
36855  * Copyright(c) 2006-2007, Ext JS, LLC.
36856  *
36857  * Originally Released Under LGPL - original licence link has changed is not relivant.
36858  *
36859  * Fork - LGPL
36860  * <script type="text/javascript">
36861  */
36862  
36863 /**
36864  * @class Roo.tree.AsyncTreeNode
36865  * @extends Roo.tree.TreeNode
36866  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36867  * @constructor
36868  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36869  */
36870  Roo.tree.AsyncTreeNode = function(config){
36871     this.loaded = false;
36872     this.loading = false;
36873     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36874     /**
36875     * @event beforeload
36876     * Fires before this node is loaded, return false to cancel
36877     * @param {Node} this This node
36878     */
36879     this.addEvents({'beforeload':true, 'load': true});
36880     /**
36881     * @event load
36882     * Fires when this node is loaded
36883     * @param {Node} this This node
36884     */
36885     /**
36886      * The loader used by this node (defaults to using the tree's defined loader)
36887      * @type TreeLoader
36888      * @property loader
36889      */
36890 };
36891 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36892     expand : function(deep, anim, callback){
36893         if(this.loading){ // if an async load is already running, waiting til it's done
36894             var timer;
36895             var f = function(){
36896                 if(!this.loading){ // done loading
36897                     clearInterval(timer);
36898                     this.expand(deep, anim, callback);
36899                 }
36900             }.createDelegate(this);
36901             timer = setInterval(f, 200);
36902             return;
36903         }
36904         if(!this.loaded){
36905             if(this.fireEvent("beforeload", this) === false){
36906                 return;
36907             }
36908             this.loading = true;
36909             this.ui.beforeLoad(this);
36910             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36911             if(loader){
36912                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36913                 return;
36914             }
36915         }
36916         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36917     },
36918     
36919     /**
36920      * Returns true if this node is currently loading
36921      * @return {Boolean}
36922      */
36923     isLoading : function(){
36924         return this.loading;  
36925     },
36926     
36927     loadComplete : function(deep, anim, callback){
36928         this.loading = false;
36929         this.loaded = true;
36930         this.ui.afterLoad(this);
36931         this.fireEvent("load", this);
36932         this.expand(deep, anim, callback);
36933     },
36934     
36935     /**
36936      * Returns true if this node has been loaded
36937      * @return {Boolean}
36938      */
36939     isLoaded : function(){
36940         return this.loaded;
36941     },
36942     
36943     hasChildNodes : function(){
36944         if(!this.isLeaf() && !this.loaded){
36945             return true;
36946         }else{
36947             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36948         }
36949     },
36950
36951     /**
36952      * Trigger a reload for this node
36953      * @param {Function} callback
36954      */
36955     reload : function(callback){
36956         this.collapse(false, false);
36957         while(this.firstChild){
36958             this.removeChild(this.firstChild);
36959         }
36960         this.childrenRendered = false;
36961         this.loaded = false;
36962         if(this.isHiddenRoot()){
36963             this.expanded = false;
36964         }
36965         this.expand(false, false, callback);
36966     }
36967 });/*
36968  * Based on:
36969  * Ext JS Library 1.1.1
36970  * Copyright(c) 2006-2007, Ext JS, LLC.
36971  *
36972  * Originally Released Under LGPL - original licence link has changed is not relivant.
36973  *
36974  * Fork - LGPL
36975  * <script type="text/javascript">
36976  */
36977  
36978 /**
36979  * @class Roo.tree.TreeNodeUI
36980  * @constructor
36981  * @param {Object} node The node to render
36982  * The TreeNode UI implementation is separate from the
36983  * tree implementation. Unless you are customizing the tree UI,
36984  * you should never have to use this directly.
36985  */
36986 Roo.tree.TreeNodeUI = function(node){
36987     this.node = node;
36988     this.rendered = false;
36989     this.animating = false;
36990     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36991 };
36992
36993 Roo.tree.TreeNodeUI.prototype = {
36994     removeChild : function(node){
36995         if(this.rendered){
36996             this.ctNode.removeChild(node.ui.getEl());
36997         }
36998     },
36999
37000     beforeLoad : function(){
37001          this.addClass("x-tree-node-loading");
37002     },
37003
37004     afterLoad : function(){
37005          this.removeClass("x-tree-node-loading");
37006     },
37007
37008     onTextChange : function(node, text, oldText){
37009         if(this.rendered){
37010             this.textNode.innerHTML = text;
37011         }
37012     },
37013
37014     onDisableChange : function(node, state){
37015         this.disabled = state;
37016         if(state){
37017             this.addClass("x-tree-node-disabled");
37018         }else{
37019             this.removeClass("x-tree-node-disabled");
37020         }
37021     },
37022
37023     onSelectedChange : function(state){
37024         if(state){
37025             this.focus();
37026             this.addClass("x-tree-selected");
37027         }else{
37028             //this.blur();
37029             this.removeClass("x-tree-selected");
37030         }
37031     },
37032
37033     onMove : function(tree, node, oldParent, newParent, index, refNode){
37034         this.childIndent = null;
37035         if(this.rendered){
37036             var targetNode = newParent.ui.getContainer();
37037             if(!targetNode){//target not rendered
37038                 this.holder = document.createElement("div");
37039                 this.holder.appendChild(this.wrap);
37040                 return;
37041             }
37042             var insertBefore = refNode ? refNode.ui.getEl() : null;
37043             if(insertBefore){
37044                 targetNode.insertBefore(this.wrap, insertBefore);
37045             }else{
37046                 targetNode.appendChild(this.wrap);
37047             }
37048             this.node.renderIndent(true);
37049         }
37050     },
37051
37052     addClass : function(cls){
37053         if(this.elNode){
37054             Roo.fly(this.elNode).addClass(cls);
37055         }
37056     },
37057
37058     removeClass : function(cls){
37059         if(this.elNode){
37060             Roo.fly(this.elNode).removeClass(cls);
37061         }
37062     },
37063
37064     remove : function(){
37065         if(this.rendered){
37066             this.holder = document.createElement("div");
37067             this.holder.appendChild(this.wrap);
37068         }
37069     },
37070
37071     fireEvent : function(){
37072         return this.node.fireEvent.apply(this.node, arguments);
37073     },
37074
37075     initEvents : function(){
37076         this.node.on("move", this.onMove, this);
37077         var E = Roo.EventManager;
37078         var a = this.anchor;
37079
37080         var el = Roo.fly(a, '_treeui');
37081
37082         if(Roo.isOpera){ // opera render bug ignores the CSS
37083             el.setStyle("text-decoration", "none");
37084         }
37085
37086         el.on("click", this.onClick, this);
37087         el.on("dblclick", this.onDblClick, this);
37088
37089         if(this.checkbox){
37090             Roo.EventManager.on(this.checkbox,
37091                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37092         }
37093
37094         el.on("contextmenu", this.onContextMenu, this);
37095
37096         var icon = Roo.fly(this.iconNode);
37097         icon.on("click", this.onClick, this);
37098         icon.on("dblclick", this.onDblClick, this);
37099         icon.on("contextmenu", this.onContextMenu, this);
37100         E.on(this.ecNode, "click", this.ecClick, this, true);
37101
37102         if(this.node.disabled){
37103             this.addClass("x-tree-node-disabled");
37104         }
37105         if(this.node.hidden){
37106             this.addClass("x-tree-node-disabled");
37107         }
37108         var ot = this.node.getOwnerTree();
37109         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37110         if(dd && (!this.node.isRoot || ot.rootVisible)){
37111             Roo.dd.Registry.register(this.elNode, {
37112                 node: this.node,
37113                 handles: this.getDDHandles(),
37114                 isHandle: false
37115             });
37116         }
37117     },
37118
37119     getDDHandles : function(){
37120         return [this.iconNode, this.textNode];
37121     },
37122
37123     hide : function(){
37124         if(this.rendered){
37125             this.wrap.style.display = "none";
37126         }
37127     },
37128
37129     show : function(){
37130         if(this.rendered){
37131             this.wrap.style.display = "";
37132         }
37133     },
37134
37135     onContextMenu : function(e){
37136         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37137             e.preventDefault();
37138             this.focus();
37139             this.fireEvent("contextmenu", this.node, e);
37140         }
37141     },
37142
37143     onClick : function(e){
37144         if(this.dropping){
37145             e.stopEvent();
37146             return;
37147         }
37148         if(this.fireEvent("beforeclick", this.node, e) !== false){
37149             if(!this.disabled && this.node.attributes.href){
37150                 this.fireEvent("click", this.node, e);
37151                 return;
37152             }
37153             e.preventDefault();
37154             if(this.disabled){
37155                 return;
37156             }
37157
37158             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37159                 this.node.toggle();
37160             }
37161
37162             this.fireEvent("click", this.node, e);
37163         }else{
37164             e.stopEvent();
37165         }
37166     },
37167
37168     onDblClick : function(e){
37169         e.preventDefault();
37170         if(this.disabled){
37171             return;
37172         }
37173         if(this.checkbox){
37174             this.toggleCheck();
37175         }
37176         if(!this.animating && this.node.hasChildNodes()){
37177             this.node.toggle();
37178         }
37179         this.fireEvent("dblclick", this.node, e);
37180     },
37181
37182     onCheckChange : function(){
37183         var checked = this.checkbox.checked;
37184         this.node.attributes.checked = checked;
37185         this.fireEvent('checkchange', this.node, checked);
37186     },
37187
37188     ecClick : function(e){
37189         if(!this.animating && this.node.hasChildNodes()){
37190             this.node.toggle();
37191         }
37192     },
37193
37194     startDrop : function(){
37195         this.dropping = true;
37196     },
37197
37198     // delayed drop so the click event doesn't get fired on a drop
37199     endDrop : function(){
37200        setTimeout(function(){
37201            this.dropping = false;
37202        }.createDelegate(this), 50);
37203     },
37204
37205     expand : function(){
37206         this.updateExpandIcon();
37207         this.ctNode.style.display = "";
37208     },
37209
37210     focus : function(){
37211         if(!this.node.preventHScroll){
37212             try{this.anchor.focus();
37213             }catch(e){}
37214         }else if(!Roo.isIE){
37215             try{
37216                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37217                 var l = noscroll.scrollLeft;
37218                 this.anchor.focus();
37219                 noscroll.scrollLeft = l;
37220             }catch(e){}
37221         }
37222     },
37223
37224     toggleCheck : function(value){
37225         var cb = this.checkbox;
37226         if(cb){
37227             cb.checked = (value === undefined ? !cb.checked : value);
37228         }
37229     },
37230
37231     blur : function(){
37232         try{
37233             this.anchor.blur();
37234         }catch(e){}
37235     },
37236
37237     animExpand : function(callback){
37238         var ct = Roo.get(this.ctNode);
37239         ct.stopFx();
37240         if(!this.node.hasChildNodes()){
37241             this.updateExpandIcon();
37242             this.ctNode.style.display = "";
37243             Roo.callback(callback);
37244             return;
37245         }
37246         this.animating = true;
37247         this.updateExpandIcon();
37248
37249         ct.slideIn('t', {
37250            callback : function(){
37251                this.animating = false;
37252                Roo.callback(callback);
37253             },
37254             scope: this,
37255             duration: this.node.ownerTree.duration || .25
37256         });
37257     },
37258
37259     highlight : function(){
37260         var tree = this.node.getOwnerTree();
37261         Roo.fly(this.wrap).highlight(
37262             tree.hlColor || "C3DAF9",
37263             {endColor: tree.hlBaseColor}
37264         );
37265     },
37266
37267     collapse : function(){
37268         this.updateExpandIcon();
37269         this.ctNode.style.display = "none";
37270     },
37271
37272     animCollapse : function(callback){
37273         var ct = Roo.get(this.ctNode);
37274         ct.enableDisplayMode('block');
37275         ct.stopFx();
37276
37277         this.animating = true;
37278         this.updateExpandIcon();
37279
37280         ct.slideOut('t', {
37281             callback : function(){
37282                this.animating = false;
37283                Roo.callback(callback);
37284             },
37285             scope: this,
37286             duration: this.node.ownerTree.duration || .25
37287         });
37288     },
37289
37290     getContainer : function(){
37291         return this.ctNode;
37292     },
37293
37294     getEl : function(){
37295         return this.wrap;
37296     },
37297
37298     appendDDGhost : function(ghostNode){
37299         ghostNode.appendChild(this.elNode.cloneNode(true));
37300     },
37301
37302     getDDRepairXY : function(){
37303         return Roo.lib.Dom.getXY(this.iconNode);
37304     },
37305
37306     onRender : function(){
37307         this.render();
37308     },
37309
37310     render : function(bulkRender){
37311         var n = this.node, a = n.attributes;
37312         var targetNode = n.parentNode ?
37313               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37314
37315         if(!this.rendered){
37316             this.rendered = true;
37317
37318             this.renderElements(n, a, targetNode, bulkRender);
37319
37320             if(a.qtip){
37321                if(this.textNode.setAttributeNS){
37322                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37323                    if(a.qtipTitle){
37324                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37325                    }
37326                }else{
37327                    this.textNode.setAttribute("ext:qtip", a.qtip);
37328                    if(a.qtipTitle){
37329                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37330                    }
37331                }
37332             }else if(a.qtipCfg){
37333                 a.qtipCfg.target = Roo.id(this.textNode);
37334                 Roo.QuickTips.register(a.qtipCfg);
37335             }
37336             this.initEvents();
37337             if(!this.node.expanded){
37338                 this.updateExpandIcon();
37339             }
37340         }else{
37341             if(bulkRender === true) {
37342                 targetNode.appendChild(this.wrap);
37343             }
37344         }
37345     },
37346
37347     renderElements : function(n, a, targetNode, bulkRender)
37348     {
37349         // add some indent caching, this helps performance when rendering a large tree
37350         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37351         var t = n.getOwnerTree();
37352         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37353         if (typeof(n.attributes.html) != 'undefined') {
37354             txt = n.attributes.html;
37355         }
37356         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37357         var cb = typeof a.checked == 'boolean';
37358         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37359         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37360             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37361             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37362             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37363             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37364             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37365              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37366                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37367             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37368             "</li>"];
37369
37370         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37371             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37372                                 n.nextSibling.ui.getEl(), buf.join(""));
37373         }else{
37374             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37375         }
37376
37377         this.elNode = this.wrap.childNodes[0];
37378         this.ctNode = this.wrap.childNodes[1];
37379         var cs = this.elNode.childNodes;
37380         this.indentNode = cs[0];
37381         this.ecNode = cs[1];
37382         this.iconNode = cs[2];
37383         var index = 3;
37384         if(cb){
37385             this.checkbox = cs[3];
37386             index++;
37387         }
37388         this.anchor = cs[index];
37389         this.textNode = cs[index].firstChild;
37390     },
37391
37392     getAnchor : function(){
37393         return this.anchor;
37394     },
37395
37396     getTextEl : function(){
37397         return this.textNode;
37398     },
37399
37400     getIconEl : function(){
37401         return this.iconNode;
37402     },
37403
37404     isChecked : function(){
37405         return this.checkbox ? this.checkbox.checked : false;
37406     },
37407
37408     updateExpandIcon : function(){
37409         if(this.rendered){
37410             var n = this.node, c1, c2;
37411             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37412             var hasChild = n.hasChildNodes();
37413             if(hasChild){
37414                 if(n.expanded){
37415                     cls += "-minus";
37416                     c1 = "x-tree-node-collapsed";
37417                     c2 = "x-tree-node-expanded";
37418                 }else{
37419                     cls += "-plus";
37420                     c1 = "x-tree-node-expanded";
37421                     c2 = "x-tree-node-collapsed";
37422                 }
37423                 if(this.wasLeaf){
37424                     this.removeClass("x-tree-node-leaf");
37425                     this.wasLeaf = false;
37426                 }
37427                 if(this.c1 != c1 || this.c2 != c2){
37428                     Roo.fly(this.elNode).replaceClass(c1, c2);
37429                     this.c1 = c1; this.c2 = c2;
37430                 }
37431             }else{
37432                 // this changes non-leafs into leafs if they have no children.
37433                 // it's not very rational behaviour..
37434                 
37435                 if(!this.wasLeaf && this.node.leaf){
37436                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37437                     delete this.c1;
37438                     delete this.c2;
37439                     this.wasLeaf = true;
37440                 }
37441             }
37442             var ecc = "x-tree-ec-icon "+cls;
37443             if(this.ecc != ecc){
37444                 this.ecNode.className = ecc;
37445                 this.ecc = ecc;
37446             }
37447         }
37448     },
37449
37450     getChildIndent : function(){
37451         if(!this.childIndent){
37452             var buf = [];
37453             var p = this.node;
37454             while(p){
37455                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37456                     if(!p.isLast()) {
37457                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37458                     } else {
37459                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37460                     }
37461                 }
37462                 p = p.parentNode;
37463             }
37464             this.childIndent = buf.join("");
37465         }
37466         return this.childIndent;
37467     },
37468
37469     renderIndent : function(){
37470         if(this.rendered){
37471             var indent = "";
37472             var p = this.node.parentNode;
37473             if(p){
37474                 indent = p.ui.getChildIndent();
37475             }
37476             if(this.indentMarkup != indent){ // don't rerender if not required
37477                 this.indentNode.innerHTML = indent;
37478                 this.indentMarkup = indent;
37479             }
37480             this.updateExpandIcon();
37481         }
37482     }
37483 };
37484
37485 Roo.tree.RootTreeNodeUI = function(){
37486     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37487 };
37488 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37489     render : function(){
37490         if(!this.rendered){
37491             var targetNode = this.node.ownerTree.innerCt.dom;
37492             this.node.expanded = true;
37493             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37494             this.wrap = this.ctNode = targetNode.firstChild;
37495         }
37496     },
37497     collapse : function(){
37498     },
37499     expand : function(){
37500     }
37501 });/*
37502  * Based on:
37503  * Ext JS Library 1.1.1
37504  * Copyright(c) 2006-2007, Ext JS, LLC.
37505  *
37506  * Originally Released Under LGPL - original licence link has changed is not relivant.
37507  *
37508  * Fork - LGPL
37509  * <script type="text/javascript">
37510  */
37511 /**
37512  * @class Roo.tree.TreeLoader
37513  * @extends Roo.util.Observable
37514  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37515  * nodes from a specified URL. The response must be a javascript Array definition
37516  * who's elements are node definition objects. eg:
37517  * <pre><code>
37518 {  success : true,
37519    data :      [
37520    
37521     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37522     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37523     ]
37524 }
37525
37526
37527 </code></pre>
37528  * <br><br>
37529  * The old style respose with just an array is still supported, but not recommended.
37530  * <br><br>
37531  *
37532  * A server request is sent, and child nodes are loaded only when a node is expanded.
37533  * The loading node's id is passed to the server under the parameter name "node" to
37534  * enable the server to produce the correct child nodes.
37535  * <br><br>
37536  * To pass extra parameters, an event handler may be attached to the "beforeload"
37537  * event, and the parameters specified in the TreeLoader's baseParams property:
37538  * <pre><code>
37539     myTreeLoader.on("beforeload", function(treeLoader, node) {
37540         this.baseParams.category = node.attributes.category;
37541     }, this);
37542     
37543 </code></pre>
37544  *
37545  * This would pass an HTTP parameter called "category" to the server containing
37546  * the value of the Node's "category" attribute.
37547  * @constructor
37548  * Creates a new Treeloader.
37549  * @param {Object} config A config object containing config properties.
37550  */
37551 Roo.tree.TreeLoader = function(config){
37552     this.baseParams = {};
37553     this.requestMethod = "POST";
37554     Roo.apply(this, config);
37555
37556     this.addEvents({
37557     
37558         /**
37559          * @event beforeload
37560          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37561          * @param {Object} This TreeLoader object.
37562          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37563          * @param {Object} callback The callback function specified in the {@link #load} call.
37564          */
37565         beforeload : true,
37566         /**
37567          * @event load
37568          * Fires when the node has been successfuly loaded.
37569          * @param {Object} This TreeLoader object.
37570          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37571          * @param {Object} response The response object containing the data from the server.
37572          */
37573         load : true,
37574         /**
37575          * @event loadexception
37576          * Fires if the network request failed.
37577          * @param {Object} This TreeLoader object.
37578          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37579          * @param {Object} response The response object containing the data from the server.
37580          */
37581         loadexception : true,
37582         /**
37583          * @event create
37584          * Fires before a node is created, enabling you to return custom Node types 
37585          * @param {Object} This TreeLoader object.
37586          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37587          */
37588         create : true
37589     });
37590
37591     Roo.tree.TreeLoader.superclass.constructor.call(this);
37592 };
37593
37594 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37595     /**
37596     * @cfg {String} dataUrl The URL from which to request a Json string which
37597     * specifies an array of node definition object representing the child nodes
37598     * to be loaded.
37599     */
37600     /**
37601     * @cfg {String} requestMethod either GET or POST
37602     * defaults to POST (due to BC)
37603     * to be loaded.
37604     */
37605     /**
37606     * @cfg {Object} baseParams (optional) An object containing properties which
37607     * specify HTTP parameters to be passed to each request for child nodes.
37608     */
37609     /**
37610     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37611     * created by this loader. If the attributes sent by the server have an attribute in this object,
37612     * they take priority.
37613     */
37614     /**
37615     * @cfg {Object} uiProviders (optional) An object containing properties which
37616     * 
37617     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37618     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37619     * <i>uiProvider</i> attribute of a returned child node is a string rather
37620     * than a reference to a TreeNodeUI implementation, this that string value
37621     * is used as a property name in the uiProviders object. You can define the provider named
37622     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37623     */
37624     uiProviders : {},
37625
37626     /**
37627     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37628     * child nodes before loading.
37629     */
37630     clearOnLoad : true,
37631
37632     /**
37633     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37634     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37635     * Grid query { data : [ .....] }
37636     */
37637     
37638     root : false,
37639      /**
37640     * @cfg {String} queryParam (optional) 
37641     * Name of the query as it will be passed on the querystring (defaults to 'node')
37642     * eg. the request will be ?node=[id]
37643     */
37644     
37645     
37646     queryParam: false,
37647     
37648     /**
37649      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37650      * This is called automatically when a node is expanded, but may be used to reload
37651      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37652      * @param {Roo.tree.TreeNode} node
37653      * @param {Function} callback
37654      */
37655     load : function(node, callback){
37656         if(this.clearOnLoad){
37657             while(node.firstChild){
37658                 node.removeChild(node.firstChild);
37659             }
37660         }
37661         if(node.attributes.children){ // preloaded json children
37662             var cs = node.attributes.children;
37663             for(var i = 0, len = cs.length; i < len; i++){
37664                 node.appendChild(this.createNode(cs[i]));
37665             }
37666             if(typeof callback == "function"){
37667                 callback();
37668             }
37669         }else if(this.dataUrl){
37670             this.requestData(node, callback);
37671         }
37672     },
37673
37674     getParams: function(node){
37675         var buf = [], bp = this.baseParams;
37676         for(var key in bp){
37677             if(typeof bp[key] != "function"){
37678                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37679             }
37680         }
37681         var n = this.queryParam === false ? 'node' : this.queryParam;
37682         buf.push(n + "=", encodeURIComponent(node.id));
37683         return buf.join("");
37684     },
37685
37686     requestData : function(node, callback){
37687         if(this.fireEvent("beforeload", this, node, callback) !== false){
37688             this.transId = Roo.Ajax.request({
37689                 method:this.requestMethod,
37690                 url: this.dataUrl||this.url,
37691                 success: this.handleResponse,
37692                 failure: this.handleFailure,
37693                 scope: this,
37694                 argument: {callback: callback, node: node},
37695                 params: this.getParams(node)
37696             });
37697         }else{
37698             // if the load is cancelled, make sure we notify
37699             // the node that we are done
37700             if(typeof callback == "function"){
37701                 callback();
37702             }
37703         }
37704     },
37705
37706     isLoading : function(){
37707         return this.transId ? true : false;
37708     },
37709
37710     abort : function(){
37711         if(this.isLoading()){
37712             Roo.Ajax.abort(this.transId);
37713         }
37714     },
37715
37716     // private
37717     createNode : function(attr)
37718     {
37719         // apply baseAttrs, nice idea Corey!
37720         if(this.baseAttrs){
37721             Roo.applyIf(attr, this.baseAttrs);
37722         }
37723         if(this.applyLoader !== false){
37724             attr.loader = this;
37725         }
37726         // uiProvider = depreciated..
37727         
37728         if(typeof(attr.uiProvider) == 'string'){
37729            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37730                 /**  eval:var:attr */ eval(attr.uiProvider);
37731         }
37732         if(typeof(this.uiProviders['default']) != 'undefined') {
37733             attr.uiProvider = this.uiProviders['default'];
37734         }
37735         
37736         this.fireEvent('create', this, attr);
37737         
37738         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37739         return(attr.leaf ?
37740                         new Roo.tree.TreeNode(attr) :
37741                         new Roo.tree.AsyncTreeNode(attr));
37742     },
37743
37744     processResponse : function(response, node, callback)
37745     {
37746         var json = response.responseText;
37747         try {
37748             
37749             var o = Roo.decode(json);
37750             
37751             if (this.root === false && typeof(o.success) != undefined) {
37752                 this.root = 'data'; // the default behaviour for list like data..
37753                 }
37754                 
37755             if (this.root !== false &&  !o.success) {
37756                 // it's a failure condition.
37757                 var a = response.argument;
37758                 this.fireEvent("loadexception", this, a.node, response);
37759                 Roo.log("Load failed - should have a handler really");
37760                 return;
37761             }
37762             
37763             
37764             
37765             if (this.root !== false) {
37766                  o = o[this.root];
37767             }
37768             
37769             for(var i = 0, len = o.length; i < len; i++){
37770                 var n = this.createNode(o[i]);
37771                 if(n){
37772                     node.appendChild(n);
37773                 }
37774             }
37775             if(typeof callback == "function"){
37776                 callback(this, node);
37777             }
37778         }catch(e){
37779             this.handleFailure(response);
37780         }
37781     },
37782
37783     handleResponse : function(response){
37784         this.transId = false;
37785         var a = response.argument;
37786         this.processResponse(response, a.node, a.callback);
37787         this.fireEvent("load", this, a.node, response);
37788     },
37789
37790     handleFailure : function(response)
37791     {
37792         // should handle failure better..
37793         this.transId = false;
37794         var a = response.argument;
37795         this.fireEvent("loadexception", this, a.node, response);
37796         if(typeof a.callback == "function"){
37797             a.callback(this, a.node);
37798         }
37799     }
37800 });/*
37801  * Based on:
37802  * Ext JS Library 1.1.1
37803  * Copyright(c) 2006-2007, Ext JS, LLC.
37804  *
37805  * Originally Released Under LGPL - original licence link has changed is not relivant.
37806  *
37807  * Fork - LGPL
37808  * <script type="text/javascript">
37809  */
37810
37811 /**
37812 * @class Roo.tree.TreeFilter
37813 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37814 * @param {TreePanel} tree
37815 * @param {Object} config (optional)
37816  */
37817 Roo.tree.TreeFilter = function(tree, config){
37818     this.tree = tree;
37819     this.filtered = {};
37820     Roo.apply(this, config);
37821 };
37822
37823 Roo.tree.TreeFilter.prototype = {
37824     clearBlank:false,
37825     reverse:false,
37826     autoClear:false,
37827     remove:false,
37828
37829      /**
37830      * Filter the data by a specific attribute.
37831      * @param {String/RegExp} value Either string that the attribute value
37832      * should start with or a RegExp to test against the attribute
37833      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37834      * @param {TreeNode} startNode (optional) The node to start the filter at.
37835      */
37836     filter : function(value, attr, startNode){
37837         attr = attr || "text";
37838         var f;
37839         if(typeof value == "string"){
37840             var vlen = value.length;
37841             // auto clear empty filter
37842             if(vlen == 0 && this.clearBlank){
37843                 this.clear();
37844                 return;
37845             }
37846             value = value.toLowerCase();
37847             f = function(n){
37848                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37849             };
37850         }else if(value.exec){ // regex?
37851             f = function(n){
37852                 return value.test(n.attributes[attr]);
37853             };
37854         }else{
37855             throw 'Illegal filter type, must be string or regex';
37856         }
37857         this.filterBy(f, null, startNode);
37858         },
37859
37860     /**
37861      * Filter by a function. The passed function will be called with each
37862      * node in the tree (or from the startNode). If the function returns true, the node is kept
37863      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37864      * @param {Function} fn The filter function
37865      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37866      */
37867     filterBy : function(fn, scope, startNode){
37868         startNode = startNode || this.tree.root;
37869         if(this.autoClear){
37870             this.clear();
37871         }
37872         var af = this.filtered, rv = this.reverse;
37873         var f = function(n){
37874             if(n == startNode){
37875                 return true;
37876             }
37877             if(af[n.id]){
37878                 return false;
37879             }
37880             var m = fn.call(scope || n, n);
37881             if(!m || rv){
37882                 af[n.id] = n;
37883                 n.ui.hide();
37884                 return false;
37885             }
37886             return true;
37887         };
37888         startNode.cascade(f);
37889         if(this.remove){
37890            for(var id in af){
37891                if(typeof id != "function"){
37892                    var n = af[id];
37893                    if(n && n.parentNode){
37894                        n.parentNode.removeChild(n);
37895                    }
37896                }
37897            }
37898         }
37899     },
37900
37901     /**
37902      * Clears the current filter. Note: with the "remove" option
37903      * set a filter cannot be cleared.
37904      */
37905     clear : function(){
37906         var t = this.tree;
37907         var af = this.filtered;
37908         for(var id in af){
37909             if(typeof id != "function"){
37910                 var n = af[id];
37911                 if(n){
37912                     n.ui.show();
37913                 }
37914             }
37915         }
37916         this.filtered = {};
37917     }
37918 };
37919 /*
37920  * Based on:
37921  * Ext JS Library 1.1.1
37922  * Copyright(c) 2006-2007, Ext JS, LLC.
37923  *
37924  * Originally Released Under LGPL - original licence link has changed is not relivant.
37925  *
37926  * Fork - LGPL
37927  * <script type="text/javascript">
37928  */
37929  
37930
37931 /**
37932  * @class Roo.tree.TreeSorter
37933  * Provides sorting of nodes in a TreePanel
37934  * 
37935  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37936  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37937  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37938  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37939  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37940  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37941  * @constructor
37942  * @param {TreePanel} tree
37943  * @param {Object} config
37944  */
37945 Roo.tree.TreeSorter = function(tree, config){
37946     Roo.apply(this, config);
37947     tree.on("beforechildrenrendered", this.doSort, this);
37948     tree.on("append", this.updateSort, this);
37949     tree.on("insert", this.updateSort, this);
37950     
37951     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37952     var p = this.property || "text";
37953     var sortType = this.sortType;
37954     var fs = this.folderSort;
37955     var cs = this.caseSensitive === true;
37956     var leafAttr = this.leafAttr || 'leaf';
37957
37958     this.sortFn = function(n1, n2){
37959         if(fs){
37960             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37961                 return 1;
37962             }
37963             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37964                 return -1;
37965             }
37966         }
37967         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37968         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37969         if(v1 < v2){
37970                         return dsc ? +1 : -1;
37971                 }else if(v1 > v2){
37972                         return dsc ? -1 : +1;
37973         }else{
37974                 return 0;
37975         }
37976     };
37977 };
37978
37979 Roo.tree.TreeSorter.prototype = {
37980     doSort : function(node){
37981         node.sort(this.sortFn);
37982     },
37983     
37984     compareNodes : function(n1, n2){
37985         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37986     },
37987     
37988     updateSort : function(tree, node){
37989         if(node.childrenRendered){
37990             this.doSort.defer(1, this, [node]);
37991         }
37992     }
37993 };/*
37994  * Based on:
37995  * Ext JS Library 1.1.1
37996  * Copyright(c) 2006-2007, Ext JS, LLC.
37997  *
37998  * Originally Released Under LGPL - original licence link has changed is not relivant.
37999  *
38000  * Fork - LGPL
38001  * <script type="text/javascript">
38002  */
38003
38004 if(Roo.dd.DropZone){
38005     
38006 Roo.tree.TreeDropZone = function(tree, config){
38007     this.allowParentInsert = false;
38008     this.allowContainerDrop = false;
38009     this.appendOnly = false;
38010     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
38011     this.tree = tree;
38012     this.lastInsertClass = "x-tree-no-status";
38013     this.dragOverData = {};
38014 };
38015
38016 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38017     ddGroup : "TreeDD",
38018     scroll:  true,
38019     
38020     expandDelay : 1000,
38021     
38022     expandNode : function(node){
38023         if(node.hasChildNodes() && !node.isExpanded()){
38024             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38025         }
38026     },
38027     
38028     queueExpand : function(node){
38029         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38030     },
38031     
38032     cancelExpand : function(){
38033         if(this.expandProcId){
38034             clearTimeout(this.expandProcId);
38035             this.expandProcId = false;
38036         }
38037     },
38038     
38039     isValidDropPoint : function(n, pt, dd, e, data){
38040         if(!n || !data){ return false; }
38041         var targetNode = n.node;
38042         var dropNode = data.node;
38043         // default drop rules
38044         if(!(targetNode && targetNode.isTarget && pt)){
38045             return false;
38046         }
38047         if(pt == "append" && targetNode.allowChildren === false){
38048             return false;
38049         }
38050         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38051             return false;
38052         }
38053         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38054             return false;
38055         }
38056         // reuse the object
38057         var overEvent = this.dragOverData;
38058         overEvent.tree = this.tree;
38059         overEvent.target = targetNode;
38060         overEvent.data = data;
38061         overEvent.point = pt;
38062         overEvent.source = dd;
38063         overEvent.rawEvent = e;
38064         overEvent.dropNode = dropNode;
38065         overEvent.cancel = false;  
38066         var result = this.tree.fireEvent("nodedragover", overEvent);
38067         return overEvent.cancel === false && result !== false;
38068     },
38069     
38070     getDropPoint : function(e, n, dd)
38071     {
38072         var tn = n.node;
38073         if(tn.isRoot){
38074             return tn.allowChildren !== false ? "append" : false; // always append for root
38075         }
38076         var dragEl = n.ddel;
38077         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38078         var y = Roo.lib.Event.getPageY(e);
38079         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38080         
38081         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38082         var noAppend = tn.allowChildren === false;
38083         if(this.appendOnly || tn.parentNode.allowChildren === false){
38084             return noAppend ? false : "append";
38085         }
38086         var noBelow = false;
38087         if(!this.allowParentInsert){
38088             noBelow = tn.hasChildNodes() && tn.isExpanded();
38089         }
38090         var q = (b - t) / (noAppend ? 2 : 3);
38091         if(y >= t && y < (t + q)){
38092             return "above";
38093         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38094             return "below";
38095         }else{
38096             return "append";
38097         }
38098     },
38099     
38100     onNodeEnter : function(n, dd, e, data)
38101     {
38102         this.cancelExpand();
38103     },
38104     
38105     onNodeOver : function(n, dd, e, data)
38106     {
38107        
38108         var pt = this.getDropPoint(e, n, dd);
38109         var node = n.node;
38110         
38111         // auto node expand check
38112         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38113             this.queueExpand(node);
38114         }else if(pt != "append"){
38115             this.cancelExpand();
38116         }
38117         
38118         // set the insert point style on the target node
38119         var returnCls = this.dropNotAllowed;
38120         if(this.isValidDropPoint(n, pt, dd, e, data)){
38121            if(pt){
38122                var el = n.ddel;
38123                var cls;
38124                if(pt == "above"){
38125                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38126                    cls = "x-tree-drag-insert-above";
38127                }else if(pt == "below"){
38128                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38129                    cls = "x-tree-drag-insert-below";
38130                }else{
38131                    returnCls = "x-tree-drop-ok-append";
38132                    cls = "x-tree-drag-append";
38133                }
38134                if(this.lastInsertClass != cls){
38135                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38136                    this.lastInsertClass = cls;
38137                }
38138            }
38139        }
38140        return returnCls;
38141     },
38142     
38143     onNodeOut : function(n, dd, e, data){
38144         
38145         this.cancelExpand();
38146         this.removeDropIndicators(n);
38147     },
38148     
38149     onNodeDrop : function(n, dd, e, data){
38150         var point = this.getDropPoint(e, n, dd);
38151         var targetNode = n.node;
38152         targetNode.ui.startDrop();
38153         if(!this.isValidDropPoint(n, point, dd, e, data)){
38154             targetNode.ui.endDrop();
38155             return false;
38156         }
38157         // first try to find the drop node
38158         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38159         var dropEvent = {
38160             tree : this.tree,
38161             target: targetNode,
38162             data: data,
38163             point: point,
38164             source: dd,
38165             rawEvent: e,
38166             dropNode: dropNode,
38167             cancel: !dropNode   
38168         };
38169         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38170         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38171             targetNode.ui.endDrop();
38172             return false;
38173         }
38174         // allow target changing
38175         targetNode = dropEvent.target;
38176         if(point == "append" && !targetNode.isExpanded()){
38177             targetNode.expand(false, null, function(){
38178                 this.completeDrop(dropEvent);
38179             }.createDelegate(this));
38180         }else{
38181             this.completeDrop(dropEvent);
38182         }
38183         return true;
38184     },
38185     
38186     completeDrop : function(de){
38187         var ns = de.dropNode, p = de.point, t = de.target;
38188         if(!(ns instanceof Array)){
38189             ns = [ns];
38190         }
38191         var n;
38192         for(var i = 0, len = ns.length; i < len; i++){
38193             n = ns[i];
38194             if(p == "above"){
38195                 t.parentNode.insertBefore(n, t);
38196             }else if(p == "below"){
38197                 t.parentNode.insertBefore(n, t.nextSibling);
38198             }else{
38199                 t.appendChild(n);
38200             }
38201         }
38202         n.ui.focus();
38203         if(this.tree.hlDrop){
38204             n.ui.highlight();
38205         }
38206         t.ui.endDrop();
38207         this.tree.fireEvent("nodedrop", de);
38208     },
38209     
38210     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38211         if(this.tree.hlDrop){
38212             dropNode.ui.focus();
38213             dropNode.ui.highlight();
38214         }
38215         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38216     },
38217     
38218     getTree : function(){
38219         return this.tree;
38220     },
38221     
38222     removeDropIndicators : function(n){
38223         if(n && n.ddel){
38224             var el = n.ddel;
38225             Roo.fly(el).removeClass([
38226                     "x-tree-drag-insert-above",
38227                     "x-tree-drag-insert-below",
38228                     "x-tree-drag-append"]);
38229             this.lastInsertClass = "_noclass";
38230         }
38231     },
38232     
38233     beforeDragDrop : function(target, e, id){
38234         this.cancelExpand();
38235         return true;
38236     },
38237     
38238     afterRepair : function(data){
38239         if(data && Roo.enableFx){
38240             data.node.ui.highlight();
38241         }
38242         this.hideProxy();
38243     } 
38244     
38245 });
38246
38247 }
38248 /*
38249  * Based on:
38250  * Ext JS Library 1.1.1
38251  * Copyright(c) 2006-2007, Ext JS, LLC.
38252  *
38253  * Originally Released Under LGPL - original licence link has changed is not relivant.
38254  *
38255  * Fork - LGPL
38256  * <script type="text/javascript">
38257  */
38258  
38259
38260 if(Roo.dd.DragZone){
38261 Roo.tree.TreeDragZone = function(tree, config){
38262     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38263     this.tree = tree;
38264 };
38265
38266 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38267     ddGroup : "TreeDD",
38268    
38269     onBeforeDrag : function(data, e){
38270         var n = data.node;
38271         return n && n.draggable && !n.disabled;
38272     },
38273      
38274     
38275     onInitDrag : function(e){
38276         var data = this.dragData;
38277         this.tree.getSelectionModel().select(data.node);
38278         this.proxy.update("");
38279         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38280         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38281     },
38282     
38283     getRepairXY : function(e, data){
38284         return data.node.ui.getDDRepairXY();
38285     },
38286     
38287     onEndDrag : function(data, e){
38288         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38289         
38290         
38291     },
38292     
38293     onValidDrop : function(dd, e, id){
38294         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38295         this.hideProxy();
38296     },
38297     
38298     beforeInvalidDrop : function(e, id){
38299         // this scrolls the original position back into view
38300         var sm = this.tree.getSelectionModel();
38301         sm.clearSelections();
38302         sm.select(this.dragData.node);
38303     }
38304 });
38305 }/*
38306  * Based on:
38307  * Ext JS Library 1.1.1
38308  * Copyright(c) 2006-2007, Ext JS, LLC.
38309  *
38310  * Originally Released Under LGPL - original licence link has changed is not relivant.
38311  *
38312  * Fork - LGPL
38313  * <script type="text/javascript">
38314  */
38315 /**
38316  * @class Roo.tree.TreeEditor
38317  * @extends Roo.Editor
38318  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38319  * as the editor field.
38320  * @constructor
38321  * @param {Object} config (used to be the tree panel.)
38322  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38323  * 
38324  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38325  * @cfg {Roo.form.TextField} field [required] The field configuration
38326  *
38327  * 
38328  */
38329 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38330     var tree = config;
38331     var field;
38332     if (oldconfig) { // old style..
38333         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38334     } else {
38335         // new style..
38336         tree = config.tree;
38337         config.field = config.field  || {};
38338         config.field.xtype = 'TextField';
38339         field = Roo.factory(config.field, Roo.form);
38340     }
38341     config = config || {};
38342     
38343     
38344     this.addEvents({
38345         /**
38346          * @event beforenodeedit
38347          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38348          * false from the handler of this event.
38349          * @param {Editor} this
38350          * @param {Roo.tree.Node} node 
38351          */
38352         "beforenodeedit" : true
38353     });
38354     
38355     //Roo.log(config);
38356     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38357
38358     this.tree = tree;
38359
38360     tree.on('beforeclick', this.beforeNodeClick, this);
38361     tree.getTreeEl().on('mousedown', this.hide, this);
38362     this.on('complete', this.updateNode, this);
38363     this.on('beforestartedit', this.fitToTree, this);
38364     this.on('startedit', this.bindScroll, this, {delay:10});
38365     this.on('specialkey', this.onSpecialKey, this);
38366 };
38367
38368 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38369     /**
38370      * @cfg {String} alignment
38371      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38372      */
38373     alignment: "l-l",
38374     // inherit
38375     autoSize: false,
38376     /**
38377      * @cfg {Boolean} hideEl
38378      * True to hide the bound element while the editor is displayed (defaults to false)
38379      */
38380     hideEl : false,
38381     /**
38382      * @cfg {String} cls
38383      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38384      */
38385     cls: "x-small-editor x-tree-editor",
38386     /**
38387      * @cfg {Boolean} shim
38388      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38389      */
38390     shim:false,
38391     // inherit
38392     shadow:"frame",
38393     /**
38394      * @cfg {Number} maxWidth
38395      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38396      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38397      * scroll and client offsets into account prior to each edit.
38398      */
38399     maxWidth: 250,
38400
38401     editDelay : 350,
38402
38403     // private
38404     fitToTree : function(ed, el){
38405         var td = this.tree.getTreeEl().dom, nd = el.dom;
38406         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38407             td.scrollLeft = nd.offsetLeft;
38408         }
38409         var w = Math.min(
38410                 this.maxWidth,
38411                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38412         this.setSize(w, '');
38413         
38414         return this.fireEvent('beforenodeedit', this, this.editNode);
38415         
38416     },
38417
38418     // private
38419     triggerEdit : function(node){
38420         this.completeEdit();
38421         this.editNode = node;
38422         this.startEdit(node.ui.textNode, node.text);
38423     },
38424
38425     // private
38426     bindScroll : function(){
38427         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38428     },
38429
38430     // private
38431     beforeNodeClick : function(node, e){
38432         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38433         this.lastClick = new Date();
38434         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38435             e.stopEvent();
38436             this.triggerEdit(node);
38437             return false;
38438         }
38439         return true;
38440     },
38441
38442     // private
38443     updateNode : function(ed, value){
38444         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38445         this.editNode.setText(value);
38446     },
38447
38448     // private
38449     onHide : function(){
38450         Roo.tree.TreeEditor.superclass.onHide.call(this);
38451         if(this.editNode){
38452             this.editNode.ui.focus();
38453         }
38454     },
38455
38456     // private
38457     onSpecialKey : function(field, e){
38458         var k = e.getKey();
38459         if(k == e.ESC){
38460             e.stopEvent();
38461             this.cancelEdit();
38462         }else if(k == e.ENTER && !e.hasModifier()){
38463             e.stopEvent();
38464             this.completeEdit();
38465         }
38466     }
38467 });//<Script type="text/javascript">
38468 /*
38469  * Based on:
38470  * Ext JS Library 1.1.1
38471  * Copyright(c) 2006-2007, Ext JS, LLC.
38472  *
38473  * Originally Released Under LGPL - original licence link has changed is not relivant.
38474  *
38475  * Fork - LGPL
38476  * <script type="text/javascript">
38477  */
38478  
38479 /**
38480  * Not documented??? - probably should be...
38481  */
38482
38483 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38484     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38485     
38486     renderElements : function(n, a, targetNode, bulkRender){
38487         //consel.log("renderElements?");
38488         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38489
38490         var t = n.getOwnerTree();
38491         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38492         
38493         var cols = t.columns;
38494         var bw = t.borderWidth;
38495         var c = cols[0];
38496         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38497          var cb = typeof a.checked == "boolean";
38498         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38499         var colcls = 'x-t-' + tid + '-c0';
38500         var buf = [
38501             '<li class="x-tree-node">',
38502             
38503                 
38504                 '<div class="x-tree-node-el ', a.cls,'">',
38505                     // extran...
38506                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38507                 
38508                 
38509                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38510                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38511                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38512                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38513                            (a.iconCls ? ' '+a.iconCls : ''),
38514                            '" unselectable="on" />',
38515                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38516                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38517                              
38518                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38519                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38520                             '<span unselectable="on" qtip="' + tx + '">',
38521                              tx,
38522                              '</span></a>' ,
38523                     '</div>',
38524                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38525                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38526                  ];
38527         for(var i = 1, len = cols.length; i < len; i++){
38528             c = cols[i];
38529             colcls = 'x-t-' + tid + '-c' +i;
38530             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38531             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38532                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38533                       "</div>");
38534          }
38535          
38536          buf.push(
38537             '</a>',
38538             '<div class="x-clear"></div></div>',
38539             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38540             "</li>");
38541         
38542         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38543             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38544                                 n.nextSibling.ui.getEl(), buf.join(""));
38545         }else{
38546             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38547         }
38548         var el = this.wrap.firstChild;
38549         this.elRow = el;
38550         this.elNode = el.firstChild;
38551         this.ranchor = el.childNodes[1];
38552         this.ctNode = this.wrap.childNodes[1];
38553         var cs = el.firstChild.childNodes;
38554         this.indentNode = cs[0];
38555         this.ecNode = cs[1];
38556         this.iconNode = cs[2];
38557         var index = 3;
38558         if(cb){
38559             this.checkbox = cs[3];
38560             index++;
38561         }
38562         this.anchor = cs[index];
38563         
38564         this.textNode = cs[index].firstChild;
38565         
38566         //el.on("click", this.onClick, this);
38567         //el.on("dblclick", this.onDblClick, this);
38568         
38569         
38570        // console.log(this);
38571     },
38572     initEvents : function(){
38573         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38574         
38575             
38576         var a = this.ranchor;
38577
38578         var el = Roo.get(a);
38579
38580         if(Roo.isOpera){ // opera render bug ignores the CSS
38581             el.setStyle("text-decoration", "none");
38582         }
38583
38584         el.on("click", this.onClick, this);
38585         el.on("dblclick", this.onDblClick, this);
38586         el.on("contextmenu", this.onContextMenu, this);
38587         
38588     },
38589     
38590     /*onSelectedChange : function(state){
38591         if(state){
38592             this.focus();
38593             this.addClass("x-tree-selected");
38594         }else{
38595             //this.blur();
38596             this.removeClass("x-tree-selected");
38597         }
38598     },*/
38599     addClass : function(cls){
38600         if(this.elRow){
38601             Roo.fly(this.elRow).addClass(cls);
38602         }
38603         
38604     },
38605     
38606     
38607     removeClass : function(cls){
38608         if(this.elRow){
38609             Roo.fly(this.elRow).removeClass(cls);
38610         }
38611     }
38612
38613     
38614     
38615 });//<Script type="text/javascript">
38616
38617 /*
38618  * Based on:
38619  * Ext JS Library 1.1.1
38620  * Copyright(c) 2006-2007, Ext JS, LLC.
38621  *
38622  * Originally Released Under LGPL - original licence link has changed is not relivant.
38623  *
38624  * Fork - LGPL
38625  * <script type="text/javascript">
38626  */
38627  
38628
38629 /**
38630  * @class Roo.tree.ColumnTree
38631  * @extends Roo.tree.TreePanel
38632  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38633  * @cfg {int} borderWidth  compined right/left border allowance
38634  * @constructor
38635  * @param {String/HTMLElement/Element} el The container element
38636  * @param {Object} config
38637  */
38638 Roo.tree.ColumnTree =  function(el, config)
38639 {
38640    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38641    this.addEvents({
38642         /**
38643         * @event resize
38644         * Fire this event on a container when it resizes
38645         * @param {int} w Width
38646         * @param {int} h Height
38647         */
38648        "resize" : true
38649     });
38650     this.on('resize', this.onResize, this);
38651 };
38652
38653 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38654     //lines:false,
38655     
38656     
38657     borderWidth: Roo.isBorderBox ? 0 : 2, 
38658     headEls : false,
38659     
38660     render : function(){
38661         // add the header.....
38662        
38663         Roo.tree.ColumnTree.superclass.render.apply(this);
38664         
38665         this.el.addClass('x-column-tree');
38666         
38667         this.headers = this.el.createChild(
38668             {cls:'x-tree-headers'},this.innerCt.dom);
38669    
38670         var cols = this.columns, c;
38671         var totalWidth = 0;
38672         this.headEls = [];
38673         var  len = cols.length;
38674         for(var i = 0; i < len; i++){
38675              c = cols[i];
38676              totalWidth += c.width;
38677             this.headEls.push(this.headers.createChild({
38678                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38679                  cn: {
38680                      cls:'x-tree-hd-text',
38681                      html: c.header
38682                  },
38683                  style:'width:'+(c.width-this.borderWidth)+'px;'
38684              }));
38685         }
38686         this.headers.createChild({cls:'x-clear'});
38687         // prevent floats from wrapping when clipped
38688         this.headers.setWidth(totalWidth);
38689         //this.innerCt.setWidth(totalWidth);
38690         this.innerCt.setStyle({ overflow: 'auto' });
38691         this.onResize(this.width, this.height);
38692              
38693         
38694     },
38695     onResize : function(w,h)
38696     {
38697         this.height = h;
38698         this.width = w;
38699         // resize cols..
38700         this.innerCt.setWidth(this.width);
38701         this.innerCt.setHeight(this.height-20);
38702         
38703         // headers...
38704         var cols = this.columns, c;
38705         var totalWidth = 0;
38706         var expEl = false;
38707         var len = cols.length;
38708         for(var i = 0; i < len; i++){
38709             c = cols[i];
38710             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38711                 // it's the expander..
38712                 expEl  = this.headEls[i];
38713                 continue;
38714             }
38715             totalWidth += c.width;
38716             
38717         }
38718         if (expEl) {
38719             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38720         }
38721         this.headers.setWidth(w-20);
38722
38723         
38724         
38725         
38726     }
38727 });
38728 /*
38729  * Based on:
38730  * Ext JS Library 1.1.1
38731  * Copyright(c) 2006-2007, Ext JS, LLC.
38732  *
38733  * Originally Released Under LGPL - original licence link has changed is not relivant.
38734  *
38735  * Fork - LGPL
38736  * <script type="text/javascript">
38737  */
38738  
38739 /**
38740  * @class Roo.menu.Menu
38741  * @extends Roo.util.Observable
38742  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38743  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38744  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38745  * @constructor
38746  * Creates a new Menu
38747  * @param {Object} config Configuration options
38748  */
38749 Roo.menu.Menu = function(config){
38750     
38751     Roo.menu.Menu.superclass.constructor.call(this, config);
38752     
38753     this.id = this.id || Roo.id();
38754     this.addEvents({
38755         /**
38756          * @event beforeshow
38757          * Fires before this menu is displayed
38758          * @param {Roo.menu.Menu} this
38759          */
38760         beforeshow : true,
38761         /**
38762          * @event beforehide
38763          * Fires before this menu is hidden
38764          * @param {Roo.menu.Menu} this
38765          */
38766         beforehide : true,
38767         /**
38768          * @event show
38769          * Fires after this menu is displayed
38770          * @param {Roo.menu.Menu} this
38771          */
38772         show : true,
38773         /**
38774          * @event hide
38775          * Fires after this menu is hidden
38776          * @param {Roo.menu.Menu} this
38777          */
38778         hide : true,
38779         /**
38780          * @event click
38781          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38782          * @param {Roo.menu.Menu} this
38783          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38784          * @param {Roo.EventObject} e
38785          */
38786         click : true,
38787         /**
38788          * @event mouseover
38789          * Fires when the mouse is hovering over this menu
38790          * @param {Roo.menu.Menu} this
38791          * @param {Roo.EventObject} e
38792          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38793          */
38794         mouseover : true,
38795         /**
38796          * @event mouseout
38797          * Fires when the mouse exits this menu
38798          * @param {Roo.menu.Menu} this
38799          * @param {Roo.EventObject} e
38800          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38801          */
38802         mouseout : true,
38803         /**
38804          * @event itemclick
38805          * Fires when a menu item contained in this menu is clicked
38806          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38807          * @param {Roo.EventObject} e
38808          */
38809         itemclick: true
38810     });
38811     if (this.registerMenu) {
38812         Roo.menu.MenuMgr.register(this);
38813     }
38814     
38815     var mis = this.items;
38816     this.items = new Roo.util.MixedCollection();
38817     if(mis){
38818         this.add.apply(this, mis);
38819     }
38820 };
38821
38822 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38823     /**
38824      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38825      */
38826     minWidth : 120,
38827     /**
38828      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38829      * for bottom-right shadow (defaults to "sides")
38830      */
38831     shadow : "sides",
38832     /**
38833      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38834      * this menu (defaults to "tl-tr?")
38835      */
38836     subMenuAlign : "tl-tr?",
38837     /**
38838      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38839      * relative to its element of origin (defaults to "tl-bl?")
38840      */
38841     defaultAlign : "tl-bl?",
38842     /**
38843      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38844      */
38845     allowOtherMenus : false,
38846     /**
38847      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38848      */
38849     registerMenu : true,
38850
38851     hidden:true,
38852
38853     // private
38854     render : function(){
38855         if(this.el){
38856             return;
38857         }
38858         var el = this.el = new Roo.Layer({
38859             cls: "x-menu",
38860             shadow:this.shadow,
38861             constrain: false,
38862             parentEl: this.parentEl || document.body,
38863             zindex:15000
38864         });
38865
38866         this.keyNav = new Roo.menu.MenuNav(this);
38867
38868         if(this.plain){
38869             el.addClass("x-menu-plain");
38870         }
38871         if(this.cls){
38872             el.addClass(this.cls);
38873         }
38874         // generic focus element
38875         this.focusEl = el.createChild({
38876             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38877         });
38878         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38879         //disabling touch- as it's causing issues ..
38880         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38881         ul.on('click'   , this.onClick, this);
38882         
38883         
38884         ul.on("mouseover", this.onMouseOver, this);
38885         ul.on("mouseout", this.onMouseOut, this);
38886         this.items.each(function(item){
38887             if (item.hidden) {
38888                 return;
38889             }
38890             
38891             var li = document.createElement("li");
38892             li.className = "x-menu-list-item";
38893             ul.dom.appendChild(li);
38894             item.render(li, this);
38895         }, this);
38896         this.ul = ul;
38897         this.autoWidth();
38898     },
38899
38900     // private
38901     autoWidth : function(){
38902         var el = this.el, ul = this.ul;
38903         if(!el){
38904             return;
38905         }
38906         var w = this.width;
38907         if(w){
38908             el.setWidth(w);
38909         }else if(Roo.isIE){
38910             el.setWidth(this.minWidth);
38911             var t = el.dom.offsetWidth; // force recalc
38912             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38913         }
38914     },
38915
38916     // private
38917     delayAutoWidth : function(){
38918         if(this.rendered){
38919             if(!this.awTask){
38920                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38921             }
38922             this.awTask.delay(20);
38923         }
38924     },
38925
38926     // private
38927     findTargetItem : function(e){
38928         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38929         if(t && t.menuItemId){
38930             return this.items.get(t.menuItemId);
38931         }
38932     },
38933
38934     // private
38935     onClick : function(e){
38936         Roo.log("menu.onClick");
38937         var t = this.findTargetItem(e);
38938         if(!t){
38939             return;
38940         }
38941         Roo.log(e);
38942         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38943             if(t == this.activeItem && t.shouldDeactivate(e)){
38944                 this.activeItem.deactivate();
38945                 delete this.activeItem;
38946                 return;
38947             }
38948             if(t.canActivate){
38949                 this.setActiveItem(t, true);
38950             }
38951             return;
38952             
38953             
38954         }
38955         
38956         t.onClick(e);
38957         this.fireEvent("click", this, t, e);
38958     },
38959
38960     // private
38961     setActiveItem : function(item, autoExpand){
38962         if(item != this.activeItem){
38963             if(this.activeItem){
38964                 this.activeItem.deactivate();
38965             }
38966             this.activeItem = item;
38967             item.activate(autoExpand);
38968         }else if(autoExpand){
38969             item.expandMenu();
38970         }
38971     },
38972
38973     // private
38974     tryActivate : function(start, step){
38975         var items = this.items;
38976         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38977             var item = items.get(i);
38978             if(!item.disabled && item.canActivate){
38979                 this.setActiveItem(item, false);
38980                 return item;
38981             }
38982         }
38983         return false;
38984     },
38985
38986     // private
38987     onMouseOver : function(e){
38988         var t;
38989         if(t = this.findTargetItem(e)){
38990             if(t.canActivate && !t.disabled){
38991                 this.setActiveItem(t, true);
38992             }
38993         }
38994         this.fireEvent("mouseover", this, e, t);
38995     },
38996
38997     // private
38998     onMouseOut : function(e){
38999         var t;
39000         if(t = this.findTargetItem(e)){
39001             if(t == this.activeItem && t.shouldDeactivate(e)){
39002                 this.activeItem.deactivate();
39003                 delete this.activeItem;
39004             }
39005         }
39006         this.fireEvent("mouseout", this, e, t);
39007     },
39008
39009     /**
39010      * Read-only.  Returns true if the menu is currently displayed, else false.
39011      * @type Boolean
39012      */
39013     isVisible : function(){
39014         return this.el && !this.hidden;
39015     },
39016
39017     /**
39018      * Displays this menu relative to another element
39019      * @param {String/HTMLElement/Roo.Element} element The element to align to
39020      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39021      * the element (defaults to this.defaultAlign)
39022      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39023      */
39024     show : function(el, pos, parentMenu){
39025         this.parentMenu = parentMenu;
39026         if(!this.el){
39027             this.render();
39028         }
39029         this.fireEvent("beforeshow", this);
39030         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39031     },
39032
39033     /**
39034      * Displays this menu at a specific xy position
39035      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39036      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39037      */
39038     showAt : function(xy, parentMenu, /* private: */_e){
39039         this.parentMenu = parentMenu;
39040         if(!this.el){
39041             this.render();
39042         }
39043         if(_e !== false){
39044             this.fireEvent("beforeshow", this);
39045             xy = this.el.adjustForConstraints(xy);
39046         }
39047         this.el.setXY(xy);
39048         this.el.show();
39049         this.hidden = false;
39050         this.focus();
39051         this.fireEvent("show", this);
39052     },
39053
39054     focus : function(){
39055         if(!this.hidden){
39056             this.doFocus.defer(50, this);
39057         }
39058     },
39059
39060     doFocus : function(){
39061         if(!this.hidden){
39062             this.focusEl.focus();
39063         }
39064     },
39065
39066     /**
39067      * Hides this menu and optionally all parent menus
39068      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39069      */
39070     hide : function(deep){
39071         if(this.el && this.isVisible()){
39072             this.fireEvent("beforehide", this);
39073             if(this.activeItem){
39074                 this.activeItem.deactivate();
39075                 this.activeItem = null;
39076             }
39077             this.el.hide();
39078             this.hidden = true;
39079             this.fireEvent("hide", this);
39080         }
39081         if(deep === true && this.parentMenu){
39082             this.parentMenu.hide(true);
39083         }
39084     },
39085
39086     /**
39087      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39088      * Any of the following are valid:
39089      * <ul>
39090      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39091      * <li>An HTMLElement object which will be converted to a menu item</li>
39092      * <li>A menu item config object that will be created as a new menu item</li>
39093      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39094      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39095      * </ul>
39096      * Usage:
39097      * <pre><code>
39098 // Create the menu
39099 var menu = new Roo.menu.Menu();
39100
39101 // Create a menu item to add by reference
39102 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39103
39104 // Add a bunch of items at once using different methods.
39105 // Only the last item added will be returned.
39106 var item = menu.add(
39107     menuItem,                // add existing item by ref
39108     'Dynamic Item',          // new TextItem
39109     '-',                     // new separator
39110     { text: 'Config Item' }  // new item by config
39111 );
39112 </code></pre>
39113      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39114      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39115      */
39116     add : function(){
39117         var a = arguments, l = a.length, item;
39118         for(var i = 0; i < l; i++){
39119             var el = a[i];
39120             if ((typeof(el) == "object") && el.xtype && el.xns) {
39121                 el = Roo.factory(el, Roo.menu);
39122             }
39123             
39124             if(el.render){ // some kind of Item
39125                 item = this.addItem(el);
39126             }else if(typeof el == "string"){ // string
39127                 if(el == "separator" || el == "-"){
39128                     item = this.addSeparator();
39129                 }else{
39130                     item = this.addText(el);
39131                 }
39132             }else if(el.tagName || el.el){ // element
39133                 item = this.addElement(el);
39134             }else if(typeof el == "object"){ // must be menu item config?
39135                 item = this.addMenuItem(el);
39136             }
39137         }
39138         return item;
39139     },
39140
39141     /**
39142      * Returns this menu's underlying {@link Roo.Element} object
39143      * @return {Roo.Element} The element
39144      */
39145     getEl : function(){
39146         if(!this.el){
39147             this.render();
39148         }
39149         return this.el;
39150     },
39151
39152     /**
39153      * Adds a separator bar to the menu
39154      * @return {Roo.menu.Item} The menu item that was added
39155      */
39156     addSeparator : function(){
39157         return this.addItem(new Roo.menu.Separator());
39158     },
39159
39160     /**
39161      * Adds an {@link Roo.Element} object to the menu
39162      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39163      * @return {Roo.menu.Item} The menu item that was added
39164      */
39165     addElement : function(el){
39166         return this.addItem(new Roo.menu.BaseItem(el));
39167     },
39168
39169     /**
39170      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39171      * @param {Roo.menu.Item} item The menu item to add
39172      * @return {Roo.menu.Item} The menu item that was added
39173      */
39174     addItem : function(item){
39175         this.items.add(item);
39176         if(this.ul){
39177             var li = document.createElement("li");
39178             li.className = "x-menu-list-item";
39179             this.ul.dom.appendChild(li);
39180             item.render(li, this);
39181             this.delayAutoWidth();
39182         }
39183         return item;
39184     },
39185
39186     /**
39187      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39188      * @param {Object} config A MenuItem config object
39189      * @return {Roo.menu.Item} The menu item that was added
39190      */
39191     addMenuItem : function(config){
39192         if(!(config instanceof Roo.menu.Item)){
39193             if(typeof config.checked == "boolean"){ // must be check menu item config?
39194                 config = new Roo.menu.CheckItem(config);
39195             }else{
39196                 config = new Roo.menu.Item(config);
39197             }
39198         }
39199         return this.addItem(config);
39200     },
39201
39202     /**
39203      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39204      * @param {String} text The text to display in the menu item
39205      * @return {Roo.menu.Item} The menu item that was added
39206      */
39207     addText : function(text){
39208         return this.addItem(new Roo.menu.TextItem({ text : text }));
39209     },
39210
39211     /**
39212      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39213      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39214      * @param {Roo.menu.Item} item The menu item to add
39215      * @return {Roo.menu.Item} The menu item that was added
39216      */
39217     insert : function(index, item){
39218         this.items.insert(index, item);
39219         if(this.ul){
39220             var li = document.createElement("li");
39221             li.className = "x-menu-list-item";
39222             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39223             item.render(li, this);
39224             this.delayAutoWidth();
39225         }
39226         return item;
39227     },
39228
39229     /**
39230      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39231      * @param {Roo.menu.Item} item The menu item to remove
39232      */
39233     remove : function(item){
39234         this.items.removeKey(item.id);
39235         item.destroy();
39236     },
39237
39238     /**
39239      * Removes and destroys all items in the menu
39240      */
39241     removeAll : function(){
39242         var f;
39243         while(f = this.items.first()){
39244             this.remove(f);
39245         }
39246     }
39247 });
39248
39249 // MenuNav is a private utility class used internally by the Menu
39250 Roo.menu.MenuNav = function(menu){
39251     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39252     this.scope = this.menu = menu;
39253 };
39254
39255 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39256     doRelay : function(e, h){
39257         var k = e.getKey();
39258         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39259             this.menu.tryActivate(0, 1);
39260             return false;
39261         }
39262         return h.call(this.scope || this, e, this.menu);
39263     },
39264
39265     up : function(e, m){
39266         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39267             m.tryActivate(m.items.length-1, -1);
39268         }
39269     },
39270
39271     down : function(e, m){
39272         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39273             m.tryActivate(0, 1);
39274         }
39275     },
39276
39277     right : function(e, m){
39278         if(m.activeItem){
39279             m.activeItem.expandMenu(true);
39280         }
39281     },
39282
39283     left : function(e, m){
39284         m.hide();
39285         if(m.parentMenu && m.parentMenu.activeItem){
39286             m.parentMenu.activeItem.activate();
39287         }
39288     },
39289
39290     enter : function(e, m){
39291         if(m.activeItem){
39292             e.stopPropagation();
39293             m.activeItem.onClick(e);
39294             m.fireEvent("click", this, m.activeItem);
39295             return true;
39296         }
39297     }
39298 });/*
39299  * Based on:
39300  * Ext JS Library 1.1.1
39301  * Copyright(c) 2006-2007, Ext JS, LLC.
39302  *
39303  * Originally Released Under LGPL - original licence link has changed is not relivant.
39304  *
39305  * Fork - LGPL
39306  * <script type="text/javascript">
39307  */
39308  
39309 /**
39310  * @class Roo.menu.MenuMgr
39311  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39312  * @static
39313  */
39314 Roo.menu.MenuMgr = function(){
39315    var menus, active, groups = {}, attached = false, lastShow = new Date();
39316
39317    // private - called when first menu is created
39318    function init(){
39319        menus = {};
39320        active = new Roo.util.MixedCollection();
39321        Roo.get(document).addKeyListener(27, function(){
39322            if(active.length > 0){
39323                hideAll();
39324            }
39325        });
39326    }
39327
39328    // private
39329    function hideAll(){
39330        if(active && active.length > 0){
39331            var c = active.clone();
39332            c.each(function(m){
39333                m.hide();
39334            });
39335        }
39336    }
39337
39338    // private
39339    function onHide(m){
39340        active.remove(m);
39341        if(active.length < 1){
39342            Roo.get(document).un("mousedown", onMouseDown);
39343            attached = false;
39344        }
39345    }
39346
39347    // private
39348    function onShow(m){
39349        var last = active.last();
39350        lastShow = new Date();
39351        active.add(m);
39352        if(!attached){
39353            Roo.get(document).on("mousedown", onMouseDown);
39354            attached = true;
39355        }
39356        if(m.parentMenu){
39357           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39358           m.parentMenu.activeChild = m;
39359        }else if(last && last.isVisible()){
39360           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39361        }
39362    }
39363
39364    // private
39365    function onBeforeHide(m){
39366        if(m.activeChild){
39367            m.activeChild.hide();
39368        }
39369        if(m.autoHideTimer){
39370            clearTimeout(m.autoHideTimer);
39371            delete m.autoHideTimer;
39372        }
39373    }
39374
39375    // private
39376    function onBeforeShow(m){
39377        var pm = m.parentMenu;
39378        if(!pm && !m.allowOtherMenus){
39379            hideAll();
39380        }else if(pm && pm.activeChild && active != m){
39381            pm.activeChild.hide();
39382        }
39383    }
39384
39385    // private
39386    function onMouseDown(e){
39387        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39388            hideAll();
39389        }
39390    }
39391
39392    // private
39393    function onBeforeCheck(mi, state){
39394        if(state){
39395            var g = groups[mi.group];
39396            for(var i = 0, l = g.length; i < l; i++){
39397                if(g[i] != mi){
39398                    g[i].setChecked(false);
39399                }
39400            }
39401        }
39402    }
39403
39404    return {
39405
39406        /**
39407         * Hides all menus that are currently visible
39408         */
39409        hideAll : function(){
39410             hideAll();  
39411        },
39412
39413        // private
39414        register : function(menu){
39415            if(!menus){
39416                init();
39417            }
39418            menus[menu.id] = menu;
39419            menu.on("beforehide", onBeforeHide);
39420            menu.on("hide", onHide);
39421            menu.on("beforeshow", onBeforeShow);
39422            menu.on("show", onShow);
39423            var g = menu.group;
39424            if(g && menu.events["checkchange"]){
39425                if(!groups[g]){
39426                    groups[g] = [];
39427                }
39428                groups[g].push(menu);
39429                menu.on("checkchange", onCheck);
39430            }
39431        },
39432
39433         /**
39434          * Returns a {@link Roo.menu.Menu} object
39435          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39436          * be used to generate and return a new Menu instance.
39437          */
39438        get : function(menu){
39439            if(typeof menu == "string"){ // menu id
39440                return menus[menu];
39441            }else if(menu.events){  // menu instance
39442                return menu;
39443            }else if(typeof menu.length == 'number'){ // array of menu items?
39444                return new Roo.menu.Menu({items:menu});
39445            }else{ // otherwise, must be a config
39446                return new Roo.menu.Menu(menu);
39447            }
39448        },
39449
39450        // private
39451        unregister : function(menu){
39452            delete menus[menu.id];
39453            menu.un("beforehide", onBeforeHide);
39454            menu.un("hide", onHide);
39455            menu.un("beforeshow", onBeforeShow);
39456            menu.un("show", onShow);
39457            var g = menu.group;
39458            if(g && menu.events["checkchange"]){
39459                groups[g].remove(menu);
39460                menu.un("checkchange", onCheck);
39461            }
39462        },
39463
39464        // private
39465        registerCheckable : function(menuItem){
39466            var g = menuItem.group;
39467            if(g){
39468                if(!groups[g]){
39469                    groups[g] = [];
39470                }
39471                groups[g].push(menuItem);
39472                menuItem.on("beforecheckchange", onBeforeCheck);
39473            }
39474        },
39475
39476        // private
39477        unregisterCheckable : function(menuItem){
39478            var g = menuItem.group;
39479            if(g){
39480                groups[g].remove(menuItem);
39481                menuItem.un("beforecheckchange", onBeforeCheck);
39482            }
39483        }
39484    };
39485 }();/*
39486  * Based on:
39487  * Ext JS Library 1.1.1
39488  * Copyright(c) 2006-2007, Ext JS, LLC.
39489  *
39490  * Originally Released Under LGPL - original licence link has changed is not relivant.
39491  *
39492  * Fork - LGPL
39493  * <script type="text/javascript">
39494  */
39495  
39496
39497 /**
39498  * @class Roo.menu.BaseItem
39499  * @extends Roo.Component
39500  * @abstract
39501  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39502  * management and base configuration options shared by all menu components.
39503  * @constructor
39504  * Creates a new BaseItem
39505  * @param {Object} config Configuration options
39506  */
39507 Roo.menu.BaseItem = function(config){
39508     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39509
39510     this.addEvents({
39511         /**
39512          * @event click
39513          * Fires when this item is clicked
39514          * @param {Roo.menu.BaseItem} this
39515          * @param {Roo.EventObject} e
39516          */
39517         click: true,
39518         /**
39519          * @event activate
39520          * Fires when this item is activated
39521          * @param {Roo.menu.BaseItem} this
39522          */
39523         activate : true,
39524         /**
39525          * @event deactivate
39526          * Fires when this item is deactivated
39527          * @param {Roo.menu.BaseItem} this
39528          */
39529         deactivate : true
39530     });
39531
39532     if(this.handler){
39533         this.on("click", this.handler, this.scope, true);
39534     }
39535 };
39536
39537 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39538     /**
39539      * @cfg {Function} handler
39540      * A function that will handle the click event of this menu item (defaults to undefined)
39541      */
39542     /**
39543      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39544      */
39545     canActivate : false,
39546     
39547      /**
39548      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39549      */
39550     hidden: false,
39551     
39552     /**
39553      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39554      */
39555     activeClass : "x-menu-item-active",
39556     /**
39557      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39558      */
39559     hideOnClick : true,
39560     /**
39561      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39562      */
39563     hideDelay : 100,
39564
39565     // private
39566     ctype: "Roo.menu.BaseItem",
39567
39568     // private
39569     actionMode : "container",
39570
39571     // private
39572     render : function(container, parentMenu){
39573         this.parentMenu = parentMenu;
39574         Roo.menu.BaseItem.superclass.render.call(this, container);
39575         this.container.menuItemId = this.id;
39576     },
39577
39578     // private
39579     onRender : function(container, position){
39580         this.el = Roo.get(this.el);
39581         container.dom.appendChild(this.el.dom);
39582     },
39583
39584     // private
39585     onClick : function(e){
39586         if(!this.disabled && this.fireEvent("click", this, e) !== false
39587                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39588             this.handleClick(e);
39589         }else{
39590             e.stopEvent();
39591         }
39592     },
39593
39594     // private
39595     activate : function(){
39596         if(this.disabled){
39597             return false;
39598         }
39599         var li = this.container;
39600         li.addClass(this.activeClass);
39601         this.region = li.getRegion().adjust(2, 2, -2, -2);
39602         this.fireEvent("activate", this);
39603         return true;
39604     },
39605
39606     // private
39607     deactivate : function(){
39608         this.container.removeClass(this.activeClass);
39609         this.fireEvent("deactivate", this);
39610     },
39611
39612     // private
39613     shouldDeactivate : function(e){
39614         return !this.region || !this.region.contains(e.getPoint());
39615     },
39616
39617     // private
39618     handleClick : function(e){
39619         if(this.hideOnClick){
39620             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39621         }
39622     },
39623
39624     // private
39625     expandMenu : function(autoActivate){
39626         // do nothing
39627     },
39628
39629     // private
39630     hideMenu : function(){
39631         // do nothing
39632     }
39633 });/*
39634  * Based on:
39635  * Ext JS Library 1.1.1
39636  * Copyright(c) 2006-2007, Ext JS, LLC.
39637  *
39638  * Originally Released Under LGPL - original licence link has changed is not relivant.
39639  *
39640  * Fork - LGPL
39641  * <script type="text/javascript">
39642  */
39643  
39644 /**
39645  * @class Roo.menu.Adapter
39646  * @extends Roo.menu.BaseItem
39647  * @abstract
39648  * 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.
39649  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39650  * @constructor
39651  * Creates a new Adapter
39652  * @param {Object} config Configuration options
39653  */
39654 Roo.menu.Adapter = function(component, config){
39655     Roo.menu.Adapter.superclass.constructor.call(this, config);
39656     this.component = component;
39657 };
39658 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39659     // private
39660     canActivate : true,
39661
39662     // private
39663     onRender : function(container, position){
39664         this.component.render(container);
39665         this.el = this.component.getEl();
39666     },
39667
39668     // private
39669     activate : function(){
39670         if(this.disabled){
39671             return false;
39672         }
39673         this.component.focus();
39674         this.fireEvent("activate", this);
39675         return true;
39676     },
39677
39678     // private
39679     deactivate : function(){
39680         this.fireEvent("deactivate", this);
39681     },
39682
39683     // private
39684     disable : function(){
39685         this.component.disable();
39686         Roo.menu.Adapter.superclass.disable.call(this);
39687     },
39688
39689     // private
39690     enable : function(){
39691         this.component.enable();
39692         Roo.menu.Adapter.superclass.enable.call(this);
39693     }
39694 });/*
39695  * Based on:
39696  * Ext JS Library 1.1.1
39697  * Copyright(c) 2006-2007, Ext JS, LLC.
39698  *
39699  * Originally Released Under LGPL - original licence link has changed is not relivant.
39700  *
39701  * Fork - LGPL
39702  * <script type="text/javascript">
39703  */
39704
39705 /**
39706  * @class Roo.menu.TextItem
39707  * @extends Roo.menu.BaseItem
39708  * Adds a static text string to a menu, usually used as either a heading or group separator.
39709  * Note: old style constructor with text is still supported.
39710  * 
39711  * @constructor
39712  * Creates a new TextItem
39713  * @param {Object} cfg Configuration
39714  */
39715 Roo.menu.TextItem = function(cfg){
39716     if (typeof(cfg) == 'string') {
39717         this.text = cfg;
39718     } else {
39719         Roo.apply(this,cfg);
39720     }
39721     
39722     Roo.menu.TextItem.superclass.constructor.call(this);
39723 };
39724
39725 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39726     /**
39727      * @cfg {String} text Text to show on item.
39728      */
39729     text : '',
39730     
39731     /**
39732      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39733      */
39734     hideOnClick : false,
39735     /**
39736      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39737      */
39738     itemCls : "x-menu-text",
39739
39740     // private
39741     onRender : function(){
39742         var s = document.createElement("span");
39743         s.className = this.itemCls;
39744         s.innerHTML = this.text;
39745         this.el = s;
39746         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39747     }
39748 });/*
39749  * Based on:
39750  * Ext JS Library 1.1.1
39751  * Copyright(c) 2006-2007, Ext JS, LLC.
39752  *
39753  * Originally Released Under LGPL - original licence link has changed is not relivant.
39754  *
39755  * Fork - LGPL
39756  * <script type="text/javascript">
39757  */
39758
39759 /**
39760  * @class Roo.menu.Separator
39761  * @extends Roo.menu.BaseItem
39762  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39763  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39764  * @constructor
39765  * @param {Object} config Configuration options
39766  */
39767 Roo.menu.Separator = function(config){
39768     Roo.menu.Separator.superclass.constructor.call(this, config);
39769 };
39770
39771 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39772     /**
39773      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39774      */
39775     itemCls : "x-menu-sep",
39776     /**
39777      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39778      */
39779     hideOnClick : false,
39780
39781     // private
39782     onRender : function(li){
39783         var s = document.createElement("span");
39784         s.className = this.itemCls;
39785         s.innerHTML = "&#160;";
39786         this.el = s;
39787         li.addClass("x-menu-sep-li");
39788         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39789     }
39790 });/*
39791  * Based on:
39792  * Ext JS Library 1.1.1
39793  * Copyright(c) 2006-2007, Ext JS, LLC.
39794  *
39795  * Originally Released Under LGPL - original licence link has changed is not relivant.
39796  *
39797  * Fork - LGPL
39798  * <script type="text/javascript">
39799  */
39800 /**
39801  * @class Roo.menu.Item
39802  * @extends Roo.menu.BaseItem
39803  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39804  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39805  * activation and click handling.
39806  * @constructor
39807  * Creates a new Item
39808  * @param {Object} config Configuration options
39809  */
39810 Roo.menu.Item = function(config){
39811     Roo.menu.Item.superclass.constructor.call(this, config);
39812     if(this.menu){
39813         this.menu = Roo.menu.MenuMgr.get(this.menu);
39814     }
39815 };
39816 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39817     /**
39818      * @cfg {Roo.menu.Menu} menu
39819      * A Sub menu
39820      */
39821     /**
39822      * @cfg {String} text
39823      * The text to show on the menu item.
39824      */
39825     text: '',
39826      /**
39827      * @cfg {String} HTML to render in menu
39828      * The text to show on the menu item (HTML version).
39829      */
39830     html: '',
39831     /**
39832      * @cfg {String} icon
39833      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39834      */
39835     icon: undefined,
39836     /**
39837      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39838      */
39839     itemCls : "x-menu-item",
39840     /**
39841      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39842      */
39843     canActivate : true,
39844     /**
39845      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39846      */
39847     showDelay: 200,
39848     // doc'd in BaseItem
39849     hideDelay: 200,
39850
39851     // private
39852     ctype: "Roo.menu.Item",
39853     
39854     // private
39855     onRender : function(container, position){
39856         var el = document.createElement("a");
39857         el.hideFocus = true;
39858         el.unselectable = "on";
39859         el.href = this.href || "#";
39860         if(this.hrefTarget){
39861             el.target = this.hrefTarget;
39862         }
39863         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39864         
39865         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39866         
39867         el.innerHTML = String.format(
39868                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39869                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39870         this.el = el;
39871         Roo.menu.Item.superclass.onRender.call(this, container, position);
39872     },
39873
39874     /**
39875      * Sets the text to display in this menu item
39876      * @param {String} text The text to display
39877      * @param {Boolean} isHTML true to indicate text is pure html.
39878      */
39879     setText : function(text, isHTML){
39880         if (isHTML) {
39881             this.html = text;
39882         } else {
39883             this.text = text;
39884             this.html = '';
39885         }
39886         if(this.rendered){
39887             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39888      
39889             this.el.update(String.format(
39890                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39891                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39892             this.parentMenu.autoWidth();
39893         }
39894     },
39895
39896     // private
39897     handleClick : function(e){
39898         if(!this.href){ // if no link defined, stop the event automatically
39899             e.stopEvent();
39900         }
39901         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39902     },
39903
39904     // private
39905     activate : function(autoExpand){
39906         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39907             this.focus();
39908             if(autoExpand){
39909                 this.expandMenu();
39910             }
39911         }
39912         return true;
39913     },
39914
39915     // private
39916     shouldDeactivate : function(e){
39917         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39918             if(this.menu && this.menu.isVisible()){
39919                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39920             }
39921             return true;
39922         }
39923         return false;
39924     },
39925
39926     // private
39927     deactivate : function(){
39928         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39929         this.hideMenu();
39930     },
39931
39932     // private
39933     expandMenu : function(autoActivate){
39934         if(!this.disabled && this.menu){
39935             clearTimeout(this.hideTimer);
39936             delete this.hideTimer;
39937             if(!this.menu.isVisible() && !this.showTimer){
39938                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39939             }else if (this.menu.isVisible() && autoActivate){
39940                 this.menu.tryActivate(0, 1);
39941             }
39942         }
39943     },
39944
39945     // private
39946     deferExpand : function(autoActivate){
39947         delete this.showTimer;
39948         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39949         if(autoActivate){
39950             this.menu.tryActivate(0, 1);
39951         }
39952     },
39953
39954     // private
39955     hideMenu : function(){
39956         clearTimeout(this.showTimer);
39957         delete this.showTimer;
39958         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39959             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39960         }
39961     },
39962
39963     // private
39964     deferHide : function(){
39965         delete this.hideTimer;
39966         this.menu.hide();
39967     }
39968 });/*
39969  * Based on:
39970  * Ext JS Library 1.1.1
39971  * Copyright(c) 2006-2007, Ext JS, LLC.
39972  *
39973  * Originally Released Under LGPL - original licence link has changed is not relivant.
39974  *
39975  * Fork - LGPL
39976  * <script type="text/javascript">
39977  */
39978  
39979 /**
39980  * @class Roo.menu.CheckItem
39981  * @extends Roo.menu.Item
39982  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39983  * @constructor
39984  * Creates a new CheckItem
39985  * @param {Object} config Configuration options
39986  */
39987 Roo.menu.CheckItem = function(config){
39988     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39989     this.addEvents({
39990         /**
39991          * @event beforecheckchange
39992          * Fires before the checked value is set, providing an opportunity to cancel if needed
39993          * @param {Roo.menu.CheckItem} this
39994          * @param {Boolean} checked The new checked value that will be set
39995          */
39996         "beforecheckchange" : true,
39997         /**
39998          * @event checkchange
39999          * Fires after the checked value has been set
40000          * @param {Roo.menu.CheckItem} this
40001          * @param {Boolean} checked The checked value that was set
40002          */
40003         "checkchange" : true
40004     });
40005     if(this.checkHandler){
40006         this.on('checkchange', this.checkHandler, this.scope);
40007     }
40008 };
40009 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
40010     /**
40011      * @cfg {String} group
40012      * All check items with the same group name will automatically be grouped into a single-select
40013      * radio button group (defaults to '')
40014      */
40015     /**
40016      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40017      */
40018     itemCls : "x-menu-item x-menu-check-item",
40019     /**
40020      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40021      */
40022     groupClass : "x-menu-group-item",
40023
40024     /**
40025      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40026      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40027      * initialized with checked = true will be rendered as checked.
40028      */
40029     checked: false,
40030
40031     // private
40032     ctype: "Roo.menu.CheckItem",
40033
40034     // private
40035     onRender : function(c){
40036         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40037         if(this.group){
40038             this.el.addClass(this.groupClass);
40039         }
40040         Roo.menu.MenuMgr.registerCheckable(this);
40041         if(this.checked){
40042             this.checked = false;
40043             this.setChecked(true, true);
40044         }
40045     },
40046
40047     // private
40048     destroy : function(){
40049         if(this.rendered){
40050             Roo.menu.MenuMgr.unregisterCheckable(this);
40051         }
40052         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40053     },
40054
40055     /**
40056      * Set the checked state of this item
40057      * @param {Boolean} checked The new checked value
40058      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40059      */
40060     setChecked : function(state, suppressEvent){
40061         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40062             if(this.container){
40063                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40064             }
40065             this.checked = state;
40066             if(suppressEvent !== true){
40067                 this.fireEvent("checkchange", this, state);
40068             }
40069         }
40070     },
40071
40072     // private
40073     handleClick : function(e){
40074        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40075            this.setChecked(!this.checked);
40076        }
40077        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40078     }
40079 });/*
40080  * Based on:
40081  * Ext JS Library 1.1.1
40082  * Copyright(c) 2006-2007, Ext JS, LLC.
40083  *
40084  * Originally Released Under LGPL - original licence link has changed is not relivant.
40085  *
40086  * Fork - LGPL
40087  * <script type="text/javascript">
40088  */
40089  
40090 /**
40091  * @class Roo.menu.DateItem
40092  * @extends Roo.menu.Adapter
40093  * A menu item that wraps the {@link Roo.DatPicker} component.
40094  * @constructor
40095  * Creates a new DateItem
40096  * @param {Object} config Configuration options
40097  */
40098 Roo.menu.DateItem = function(config){
40099     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40100     /** The Roo.DatePicker object @type Roo.DatePicker */
40101     this.picker = this.component;
40102     this.addEvents({select: true});
40103     
40104     this.picker.on("render", function(picker){
40105         picker.getEl().swallowEvent("click");
40106         picker.container.addClass("x-menu-date-item");
40107     });
40108
40109     this.picker.on("select", this.onSelect, this);
40110 };
40111
40112 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40113     // private
40114     onSelect : function(picker, date){
40115         this.fireEvent("select", this, date, picker);
40116         Roo.menu.DateItem.superclass.handleClick.call(this);
40117     }
40118 });/*
40119  * Based on:
40120  * Ext JS Library 1.1.1
40121  * Copyright(c) 2006-2007, Ext JS, LLC.
40122  *
40123  * Originally Released Under LGPL - original licence link has changed is not relivant.
40124  *
40125  * Fork - LGPL
40126  * <script type="text/javascript">
40127  */
40128  
40129 /**
40130  * @class Roo.menu.ColorItem
40131  * @extends Roo.menu.Adapter
40132  * A menu item that wraps the {@link Roo.ColorPalette} component.
40133  * @constructor
40134  * Creates a new ColorItem
40135  * @param {Object} config Configuration options
40136  */
40137 Roo.menu.ColorItem = function(config){
40138     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40139     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40140     this.palette = this.component;
40141     this.relayEvents(this.palette, ["select"]);
40142     if(this.selectHandler){
40143         this.on('select', this.selectHandler, this.scope);
40144     }
40145 };
40146 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40147  * Based on:
40148  * Ext JS Library 1.1.1
40149  * Copyright(c) 2006-2007, Ext JS, LLC.
40150  *
40151  * Originally Released Under LGPL - original licence link has changed is not relivant.
40152  *
40153  * Fork - LGPL
40154  * <script type="text/javascript">
40155  */
40156  
40157
40158 /**
40159  * @class Roo.menu.DateMenu
40160  * @extends Roo.menu.Menu
40161  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40162  * @constructor
40163  * Creates a new DateMenu
40164  * @param {Object} config Configuration options
40165  */
40166 Roo.menu.DateMenu = function(config){
40167     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40168     this.plain = true;
40169     var di = new Roo.menu.DateItem(config);
40170     this.add(di);
40171     /**
40172      * The {@link Roo.DatePicker} instance for this DateMenu
40173      * @type DatePicker
40174      */
40175     this.picker = di.picker;
40176     /**
40177      * @event select
40178      * @param {DatePicker} picker
40179      * @param {Date} date
40180      */
40181     this.relayEvents(di, ["select"]);
40182     this.on('beforeshow', function(){
40183         if(this.picker){
40184             this.picker.hideMonthPicker(false);
40185         }
40186     }, this);
40187 };
40188 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40189     cls:'x-date-menu'
40190 });/*
40191  * Based on:
40192  * Ext JS Library 1.1.1
40193  * Copyright(c) 2006-2007, Ext JS, LLC.
40194  *
40195  * Originally Released Under LGPL - original licence link has changed is not relivant.
40196  *
40197  * Fork - LGPL
40198  * <script type="text/javascript">
40199  */
40200  
40201
40202 /**
40203  * @class Roo.menu.ColorMenu
40204  * @extends Roo.menu.Menu
40205  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40206  * @constructor
40207  * Creates a new ColorMenu
40208  * @param {Object} config Configuration options
40209  */
40210 Roo.menu.ColorMenu = function(config){
40211     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40212     this.plain = true;
40213     var ci = new Roo.menu.ColorItem(config);
40214     this.add(ci);
40215     /**
40216      * The {@link Roo.ColorPalette} instance for this ColorMenu
40217      * @type ColorPalette
40218      */
40219     this.palette = ci.palette;
40220     /**
40221      * @event select
40222      * @param {ColorPalette} palette
40223      * @param {String} color
40224      */
40225     this.relayEvents(ci, ["select"]);
40226 };
40227 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40228  * Based on:
40229  * Ext JS Library 1.1.1
40230  * Copyright(c) 2006-2007, Ext JS, LLC.
40231  *
40232  * Originally Released Under LGPL - original licence link has changed is not relivant.
40233  *
40234  * Fork - LGPL
40235  * <script type="text/javascript">
40236  */
40237  
40238 /**
40239  * @class Roo.form.TextItem
40240  * @extends Roo.BoxComponent
40241  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40242  * @constructor
40243  * Creates a new TextItem
40244  * @param {Object} config Configuration options
40245  */
40246 Roo.form.TextItem = function(config){
40247     Roo.form.TextItem.superclass.constructor.call(this, config);
40248 };
40249
40250 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40251     
40252     /**
40253      * @cfg {String} tag the tag for this item (default div)
40254      */
40255     tag : 'div',
40256     /**
40257      * @cfg {String} html the content for this item
40258      */
40259     html : '',
40260     
40261     getAutoCreate : function()
40262     {
40263         var cfg = {
40264             id: this.id,
40265             tag: this.tag,
40266             html: this.html,
40267             cls: 'x-form-item'
40268         };
40269         
40270         return cfg;
40271         
40272     },
40273     
40274     onRender : function(ct, position)
40275     {
40276         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40277         
40278         if(!this.el){
40279             var cfg = this.getAutoCreate();
40280             if(!cfg.name){
40281                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40282             }
40283             if (!cfg.name.length) {
40284                 delete cfg.name;
40285             }
40286             this.el = ct.createChild(cfg, position);
40287         }
40288     },
40289     /*
40290      * setHTML
40291      * @param {String} html update the Contents of the element.
40292      */
40293     setHTML : function(html)
40294     {
40295         this.fieldEl.dom.innerHTML = html;
40296     }
40297     
40298 });/*
40299  * Based on:
40300  * Ext JS Library 1.1.1
40301  * Copyright(c) 2006-2007, Ext JS, LLC.
40302  *
40303  * Originally Released Under LGPL - original licence link has changed is not relivant.
40304  *
40305  * Fork - LGPL
40306  * <script type="text/javascript">
40307  */
40308  
40309 /**
40310  * @class Roo.form.Field
40311  * @extends Roo.BoxComponent
40312  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40313  * @constructor
40314  * Creates a new Field
40315  * @param {Object} config Configuration options
40316  */
40317 Roo.form.Field = function(config){
40318     Roo.form.Field.superclass.constructor.call(this, config);
40319 };
40320
40321 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40322     /**
40323      * @cfg {String} fieldLabel Label to use when rendering a form.
40324      */
40325        /**
40326      * @cfg {String} qtip Mouse over tip
40327      */
40328      
40329     /**
40330      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40331      */
40332     invalidClass : "x-form-invalid",
40333     /**
40334      * @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")
40335      */
40336     invalidText : "The value in this field is invalid",
40337     /**
40338      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40339      */
40340     focusClass : "x-form-focus",
40341     /**
40342      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40343       automatic validation (defaults to "keyup").
40344      */
40345     validationEvent : "keyup",
40346     /**
40347      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40348      */
40349     validateOnBlur : true,
40350     /**
40351      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40352      */
40353     validationDelay : 250,
40354     /**
40355      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40356      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40357      */
40358     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40359     /**
40360      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40361      */
40362     fieldClass : "x-form-field",
40363     /**
40364      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40365      *<pre>
40366 Value         Description
40367 -----------   ----------------------------------------------------------------------
40368 qtip          Display a quick tip when the user hovers over the field
40369 title         Display a default browser title attribute popup
40370 under         Add a block div beneath the field containing the error text
40371 side          Add an error icon to the right of the field with a popup on hover
40372 [element id]  Add the error text directly to the innerHTML of the specified element
40373 </pre>
40374      */
40375     msgTarget : 'qtip',
40376     /**
40377      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40378      */
40379     msgFx : 'normal',
40380
40381     /**
40382      * @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.
40383      */
40384     readOnly : false,
40385
40386     /**
40387      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40388      */
40389     disabled : false,
40390
40391     /**
40392      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40393      */
40394     inputType : undefined,
40395     
40396     /**
40397      * @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).
40398          */
40399         tabIndex : undefined,
40400         
40401     // private
40402     isFormField : true,
40403
40404     // private
40405     hasFocus : false,
40406     /**
40407      * @property {Roo.Element} fieldEl
40408      * Element Containing the rendered Field (with label etc.)
40409      */
40410     /**
40411      * @cfg {Mixed} value A value to initialize this field with.
40412      */
40413     value : undefined,
40414
40415     /**
40416      * @cfg {String} name The field's HTML name attribute.
40417      */
40418     /**
40419      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40420      */
40421     // private
40422     loadedValue : false,
40423      
40424      
40425         // private ??
40426         initComponent : function(){
40427         Roo.form.Field.superclass.initComponent.call(this);
40428         this.addEvents({
40429             /**
40430              * @event focus
40431              * Fires when this field receives input focus.
40432              * @param {Roo.form.Field} this
40433              */
40434             focus : true,
40435             /**
40436              * @event blur
40437              * Fires when this field loses input focus.
40438              * @param {Roo.form.Field} this
40439              */
40440             blur : true,
40441             /**
40442              * @event specialkey
40443              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40444              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40445              * @param {Roo.form.Field} this
40446              * @param {Roo.EventObject} e The event object
40447              */
40448             specialkey : true,
40449             /**
40450              * @event change
40451              * Fires just before the field blurs if the field value has changed.
40452              * @param {Roo.form.Field} this
40453              * @param {Mixed} newValue The new value
40454              * @param {Mixed} oldValue The original value
40455              */
40456             change : true,
40457             /**
40458              * @event invalid
40459              * Fires after the field has been marked as invalid.
40460              * @param {Roo.form.Field} this
40461              * @param {String} msg The validation message
40462              */
40463             invalid : true,
40464             /**
40465              * @event valid
40466              * Fires after the field has been validated with no errors.
40467              * @param {Roo.form.Field} this
40468              */
40469             valid : true,
40470              /**
40471              * @event keyup
40472              * Fires after the key up
40473              * @param {Roo.form.Field} this
40474              * @param {Roo.EventObject}  e The event Object
40475              */
40476             keyup : true
40477         });
40478     },
40479
40480     /**
40481      * Returns the name attribute of the field if available
40482      * @return {String} name The field name
40483      */
40484     getName: function(){
40485          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40486     },
40487
40488     // private
40489     onRender : function(ct, position){
40490         Roo.form.Field.superclass.onRender.call(this, ct, position);
40491         if(!this.el){
40492             var cfg = this.getAutoCreate();
40493             if(!cfg.name){
40494                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40495             }
40496             if (!cfg.name.length) {
40497                 delete cfg.name;
40498             }
40499             if(this.inputType){
40500                 cfg.type = this.inputType;
40501             }
40502             this.el = ct.createChild(cfg, position);
40503         }
40504         var type = this.el.dom.type;
40505         if(type){
40506             if(type == 'password'){
40507                 type = 'text';
40508             }
40509             this.el.addClass('x-form-'+type);
40510         }
40511         if(this.readOnly){
40512             this.el.dom.readOnly = true;
40513         }
40514         if(this.tabIndex !== undefined){
40515             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40516         }
40517
40518         this.el.addClass([this.fieldClass, this.cls]);
40519         this.initValue();
40520     },
40521
40522     /**
40523      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40524      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40525      * @return {Roo.form.Field} this
40526      */
40527     applyTo : function(target){
40528         this.allowDomMove = false;
40529         this.el = Roo.get(target);
40530         this.render(this.el.dom.parentNode);
40531         return this;
40532     },
40533
40534     // private
40535     initValue : function(){
40536         if(this.value !== undefined){
40537             this.setValue(this.value);
40538         }else if(this.el.dom.value.length > 0){
40539             this.setValue(this.el.dom.value);
40540         }
40541     },
40542
40543     /**
40544      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40545      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40546      */
40547     isDirty : function() {
40548         if(this.disabled) {
40549             return false;
40550         }
40551         return String(this.getValue()) !== String(this.originalValue);
40552     },
40553
40554     /**
40555      * stores the current value in loadedValue
40556      */
40557     resetHasChanged : function()
40558     {
40559         this.loadedValue = String(this.getValue());
40560     },
40561     /**
40562      * checks the current value against the 'loaded' value.
40563      * Note - will return false if 'resetHasChanged' has not been called first.
40564      */
40565     hasChanged : function()
40566     {
40567         if(this.disabled || this.readOnly) {
40568             return false;
40569         }
40570         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40571     },
40572     
40573     
40574     
40575     // private
40576     afterRender : function(){
40577         Roo.form.Field.superclass.afterRender.call(this);
40578         this.initEvents();
40579     },
40580
40581     // private
40582     fireKey : function(e){
40583         //Roo.log('field ' + e.getKey());
40584         if(e.isNavKeyPress()){
40585             this.fireEvent("specialkey", this, e);
40586         }
40587     },
40588
40589     /**
40590      * Resets the current field value to the originally loaded value and clears any validation messages
40591      */
40592     reset : function(){
40593         this.setValue(this.resetValue);
40594         this.originalValue = this.getValue();
40595         this.clearInvalid();
40596     },
40597
40598     // private
40599     initEvents : function(){
40600         // safari killled keypress - so keydown is now used..
40601         this.el.on("keydown" , this.fireKey,  this);
40602         this.el.on("focus", this.onFocus,  this);
40603         this.el.on("blur", this.onBlur,  this);
40604         this.el.relayEvent('keyup', this);
40605
40606         // reference to original value for reset
40607         this.originalValue = this.getValue();
40608         this.resetValue =  this.getValue();
40609     },
40610
40611     // private
40612     onFocus : function(){
40613         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40614             this.el.addClass(this.focusClass);
40615         }
40616         if(!this.hasFocus){
40617             this.hasFocus = true;
40618             this.startValue = this.getValue();
40619             this.fireEvent("focus", this);
40620         }
40621     },
40622
40623     beforeBlur : Roo.emptyFn,
40624
40625     // private
40626     onBlur : function(){
40627         this.beforeBlur();
40628         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40629             this.el.removeClass(this.focusClass);
40630         }
40631         this.hasFocus = false;
40632         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40633             this.validate();
40634         }
40635         var v = this.getValue();
40636         if(String(v) !== String(this.startValue)){
40637             this.fireEvent('change', this, v, this.startValue);
40638         }
40639         this.fireEvent("blur", this);
40640     },
40641
40642     /**
40643      * Returns whether or not the field value is currently valid
40644      * @param {Boolean} preventMark True to disable marking the field invalid
40645      * @return {Boolean} True if the value is valid, else false
40646      */
40647     isValid : function(preventMark){
40648         if(this.disabled){
40649             return true;
40650         }
40651         var restore = this.preventMark;
40652         this.preventMark = preventMark === true;
40653         var v = this.validateValue(this.processValue(this.getRawValue()));
40654         this.preventMark = restore;
40655         return v;
40656     },
40657
40658     /**
40659      * Validates the field value
40660      * @return {Boolean} True if the value is valid, else false
40661      */
40662     validate : function(){
40663         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40664             this.clearInvalid();
40665             return true;
40666         }
40667         return false;
40668     },
40669
40670     processValue : function(value){
40671         return value;
40672     },
40673
40674     // private
40675     // Subclasses should provide the validation implementation by overriding this
40676     validateValue : function(value){
40677         return true;
40678     },
40679
40680     /**
40681      * Mark this field as invalid
40682      * @param {String} msg The validation message
40683      */
40684     markInvalid : function(msg){
40685         if(!this.rendered || this.preventMark){ // not rendered
40686             return;
40687         }
40688         
40689         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40690         
40691         obj.el.addClass(this.invalidClass);
40692         msg = msg || this.invalidText;
40693         switch(this.msgTarget){
40694             case 'qtip':
40695                 obj.el.dom.qtip = msg;
40696                 obj.el.dom.qclass = 'x-form-invalid-tip';
40697                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40698                     Roo.QuickTips.enable();
40699                 }
40700                 break;
40701             case 'title':
40702                 this.el.dom.title = msg;
40703                 break;
40704             case 'under':
40705                 if(!this.errorEl){
40706                     var elp = this.el.findParent('.x-form-element', 5, true);
40707                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40708                     this.errorEl.setWidth(elp.getWidth(true)-20);
40709                 }
40710                 this.errorEl.update(msg);
40711                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40712                 break;
40713             case 'side':
40714                 if(!this.errorIcon){
40715                     var elp = this.el.findParent('.x-form-element', 5, true);
40716                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40717                 }
40718                 this.alignErrorIcon();
40719                 this.errorIcon.dom.qtip = msg;
40720                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40721                 this.errorIcon.show();
40722                 this.on('resize', this.alignErrorIcon, this);
40723                 break;
40724             default:
40725                 var t = Roo.getDom(this.msgTarget);
40726                 t.innerHTML = msg;
40727                 t.style.display = this.msgDisplay;
40728                 break;
40729         }
40730         this.fireEvent('invalid', this, msg);
40731     },
40732
40733     // private
40734     alignErrorIcon : function(){
40735         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40736     },
40737
40738     /**
40739      * Clear any invalid styles/messages for this field
40740      */
40741     clearInvalid : function(){
40742         if(!this.rendered || this.preventMark){ // not rendered
40743             return;
40744         }
40745         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40746         
40747         obj.el.removeClass(this.invalidClass);
40748         switch(this.msgTarget){
40749             case 'qtip':
40750                 obj.el.dom.qtip = '';
40751                 break;
40752             case 'title':
40753                 this.el.dom.title = '';
40754                 break;
40755             case 'under':
40756                 if(this.errorEl){
40757                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40758                 }
40759                 break;
40760             case 'side':
40761                 if(this.errorIcon){
40762                     this.errorIcon.dom.qtip = '';
40763                     this.errorIcon.hide();
40764                     this.un('resize', this.alignErrorIcon, this);
40765                 }
40766                 break;
40767             default:
40768                 var t = Roo.getDom(this.msgTarget);
40769                 t.innerHTML = '';
40770                 t.style.display = 'none';
40771                 break;
40772         }
40773         this.fireEvent('valid', this);
40774     },
40775
40776     /**
40777      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40778      * @return {Mixed} value The field value
40779      */
40780     getRawValue : function(){
40781         var v = this.el.getValue();
40782         
40783         return v;
40784     },
40785
40786     /**
40787      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40788      * @return {Mixed} value The field value
40789      */
40790     getValue : function(){
40791         var v = this.el.getValue();
40792          
40793         return v;
40794     },
40795
40796     /**
40797      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40798      * @param {Mixed} value The value to set
40799      */
40800     setRawValue : function(v){
40801         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40802     },
40803
40804     /**
40805      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40806      * @param {Mixed} value The value to set
40807      */
40808     setValue : function(v){
40809         this.value = v;
40810         if(this.rendered){
40811             this.el.dom.value = (v === null || v === undefined ? '' : v);
40812              this.validate();
40813         }
40814     },
40815
40816     adjustSize : function(w, h){
40817         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40818         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40819         return s;
40820     },
40821
40822     adjustWidth : function(tag, w){
40823         tag = tag.toLowerCase();
40824         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40825             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40826                 if(tag == 'input'){
40827                     return w + 2;
40828                 }
40829                 if(tag == 'textarea'){
40830                     return w-2;
40831                 }
40832             }else if(Roo.isOpera){
40833                 if(tag == 'input'){
40834                     return w + 2;
40835                 }
40836                 if(tag == 'textarea'){
40837                     return w-2;
40838                 }
40839             }
40840         }
40841         return w;
40842     }
40843 });
40844
40845
40846 // anything other than normal should be considered experimental
40847 Roo.form.Field.msgFx = {
40848     normal : {
40849         show: function(msgEl, f){
40850             msgEl.setDisplayed('block');
40851         },
40852
40853         hide : function(msgEl, f){
40854             msgEl.setDisplayed(false).update('');
40855         }
40856     },
40857
40858     slide : {
40859         show: function(msgEl, f){
40860             msgEl.slideIn('t', {stopFx:true});
40861         },
40862
40863         hide : function(msgEl, f){
40864             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40865         }
40866     },
40867
40868     slideRight : {
40869         show: function(msgEl, f){
40870             msgEl.fixDisplay();
40871             msgEl.alignTo(f.el, 'tl-tr');
40872             msgEl.slideIn('l', {stopFx:true});
40873         },
40874
40875         hide : function(msgEl, f){
40876             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40877         }
40878     }
40879 };/*
40880  * Based on:
40881  * Ext JS Library 1.1.1
40882  * Copyright(c) 2006-2007, Ext JS, LLC.
40883  *
40884  * Originally Released Under LGPL - original licence link has changed is not relivant.
40885  *
40886  * Fork - LGPL
40887  * <script type="text/javascript">
40888  */
40889  
40890
40891 /**
40892  * @class Roo.form.TextField
40893  * @extends Roo.form.Field
40894  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40895  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40896  * @constructor
40897  * Creates a new TextField
40898  * @param {Object} config Configuration options
40899  */
40900 Roo.form.TextField = function(config){
40901     Roo.form.TextField.superclass.constructor.call(this, config);
40902     this.addEvents({
40903         /**
40904          * @event autosize
40905          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40906          * according to the default logic, but this event provides a hook for the developer to apply additional
40907          * logic at runtime to resize the field if needed.
40908              * @param {Roo.form.Field} this This text field
40909              * @param {Number} width The new field width
40910              */
40911         autosize : true
40912     });
40913 };
40914
40915 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40916     /**
40917      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40918      */
40919     grow : false,
40920     /**
40921      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40922      */
40923     growMin : 30,
40924     /**
40925      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40926      */
40927     growMax : 800,
40928     /**
40929      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40930      */
40931     vtype : null,
40932     /**
40933      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40934      */
40935     maskRe : null,
40936     /**
40937      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40938      */
40939     disableKeyFilter : false,
40940     /**
40941      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40942      */
40943     allowBlank : true,
40944     /**
40945      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40946      */
40947     minLength : 0,
40948     /**
40949      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40950      */
40951     maxLength : Number.MAX_VALUE,
40952     /**
40953      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40954      */
40955     minLengthText : "The minimum length for this field is {0}",
40956     /**
40957      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40958      */
40959     maxLengthText : "The maximum length for this field is {0}",
40960     /**
40961      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40962      */
40963     selectOnFocus : false,
40964     /**
40965      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40966      */    
40967     allowLeadingSpace : false,
40968     /**
40969      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40970      */
40971     blankText : "This field is required",
40972     /**
40973      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40974      * If available, this function will be called only after the basic validators all return true, and will be passed the
40975      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40976      */
40977     validator : null,
40978     /**
40979      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40980      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40981      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40982      */
40983     regex : null,
40984     /**
40985      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40986      */
40987     regexText : "",
40988     /**
40989      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40990      */
40991     emptyText : null,
40992    
40993
40994     // private
40995     initEvents : function()
40996     {
40997         if (this.emptyText) {
40998             this.el.attr('placeholder', this.emptyText);
40999         }
41000         
41001         Roo.form.TextField.superclass.initEvents.call(this);
41002         if(this.validationEvent == 'keyup'){
41003             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
41004             this.el.on('keyup', this.filterValidation, this);
41005         }
41006         else if(this.validationEvent !== false){
41007             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
41008         }
41009         
41010         if(this.selectOnFocus){
41011             this.on("focus", this.preFocus, this);
41012         }
41013         if (!this.allowLeadingSpace) {
41014             this.on('blur', this.cleanLeadingSpace, this);
41015         }
41016         
41017         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41018             this.el.on("keypress", this.filterKeys, this);
41019         }
41020         if(this.grow){
41021             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41022             this.el.on("click", this.autoSize,  this);
41023         }
41024         if(this.el.is('input[type=password]') && Roo.isSafari){
41025             this.el.on('keydown', this.SafariOnKeyDown, this);
41026         }
41027     },
41028
41029     processValue : function(value){
41030         if(this.stripCharsRe){
41031             var newValue = value.replace(this.stripCharsRe, '');
41032             if(newValue !== value){
41033                 this.setRawValue(newValue);
41034                 return newValue;
41035             }
41036         }
41037         return value;
41038     },
41039
41040     filterValidation : function(e){
41041         if(!e.isNavKeyPress()){
41042             this.validationTask.delay(this.validationDelay);
41043         }
41044     },
41045
41046     // private
41047     onKeyUp : function(e){
41048         if(!e.isNavKeyPress()){
41049             this.autoSize();
41050         }
41051     },
41052     // private - clean the leading white space
41053     cleanLeadingSpace : function(e)
41054     {
41055         if ( this.inputType == 'file') {
41056             return;
41057         }
41058         
41059         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41060     },
41061     /**
41062      * Resets the current field value to the originally-loaded value and clears any validation messages.
41063      *  
41064      */
41065     reset : function(){
41066         Roo.form.TextField.superclass.reset.call(this);
41067        
41068     }, 
41069     // private
41070     preFocus : function(){
41071         
41072         if(this.selectOnFocus){
41073             this.el.dom.select();
41074         }
41075     },
41076
41077     
41078     // private
41079     filterKeys : function(e){
41080         var k = e.getKey();
41081         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41082             return;
41083         }
41084         var c = e.getCharCode(), cc = String.fromCharCode(c);
41085         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41086             return;
41087         }
41088         if(!this.maskRe.test(cc)){
41089             e.stopEvent();
41090         }
41091     },
41092
41093     setValue : function(v){
41094         
41095         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41096         
41097         this.autoSize();
41098     },
41099
41100     /**
41101      * Validates a value according to the field's validation rules and marks the field as invalid
41102      * if the validation fails
41103      * @param {Mixed} value The value to validate
41104      * @return {Boolean} True if the value is valid, else false
41105      */
41106     validateValue : function(value){
41107         if(value.length < 1)  { // if it's blank
41108              if(this.allowBlank){
41109                 this.clearInvalid();
41110                 return true;
41111              }else{
41112                 this.markInvalid(this.blankText);
41113                 return false;
41114              }
41115         }
41116         if(value.length < this.minLength){
41117             this.markInvalid(String.format(this.minLengthText, this.minLength));
41118             return false;
41119         }
41120         if(value.length > this.maxLength){
41121             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41122             return false;
41123         }
41124         if(this.vtype){
41125             var vt = Roo.form.VTypes;
41126             if(!vt[this.vtype](value, this)){
41127                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41128                 return false;
41129             }
41130         }
41131         if(typeof this.validator == "function"){
41132             var msg = this.validator(value);
41133             if(msg !== true){
41134                 this.markInvalid(msg);
41135                 return false;
41136             }
41137         }
41138         if(this.regex && !this.regex.test(value)){
41139             this.markInvalid(this.regexText);
41140             return false;
41141         }
41142         return true;
41143     },
41144
41145     /**
41146      * Selects text in this field
41147      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41148      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41149      */
41150     selectText : function(start, end){
41151         var v = this.getRawValue();
41152         if(v.length > 0){
41153             start = start === undefined ? 0 : start;
41154             end = end === undefined ? v.length : end;
41155             var d = this.el.dom;
41156             if(d.setSelectionRange){
41157                 d.setSelectionRange(start, end);
41158             }else if(d.createTextRange){
41159                 var range = d.createTextRange();
41160                 range.moveStart("character", start);
41161                 range.moveEnd("character", v.length-end);
41162                 range.select();
41163             }
41164         }
41165     },
41166
41167     /**
41168      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41169      * This only takes effect if grow = true, and fires the autosize event.
41170      */
41171     autoSize : function(){
41172         if(!this.grow || !this.rendered){
41173             return;
41174         }
41175         if(!this.metrics){
41176             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41177         }
41178         var el = this.el;
41179         var v = el.dom.value;
41180         var d = document.createElement('div');
41181         d.appendChild(document.createTextNode(v));
41182         v = d.innerHTML;
41183         d = null;
41184         v += "&#160;";
41185         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41186         this.el.setWidth(w);
41187         this.fireEvent("autosize", this, w);
41188     },
41189     
41190     // private
41191     SafariOnKeyDown : function(event)
41192     {
41193         // this is a workaround for a password hang bug on chrome/ webkit.
41194         
41195         var isSelectAll = false;
41196         
41197         if(this.el.dom.selectionEnd > 0){
41198             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41199         }
41200         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41201             event.preventDefault();
41202             this.setValue('');
41203             return;
41204         }
41205         
41206         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41207             
41208             event.preventDefault();
41209             // this is very hacky as keydown always get's upper case.
41210             
41211             var cc = String.fromCharCode(event.getCharCode());
41212             
41213             
41214             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41215             
41216         }
41217         
41218         
41219     }
41220 });/*
41221  * Based on:
41222  * Ext JS Library 1.1.1
41223  * Copyright(c) 2006-2007, Ext JS, LLC.
41224  *
41225  * Originally Released Under LGPL - original licence link has changed is not relivant.
41226  *
41227  * Fork - LGPL
41228  * <script type="text/javascript">
41229  */
41230  
41231 /**
41232  * @class Roo.form.Hidden
41233  * @extends Roo.form.TextField
41234  * Simple Hidden element used on forms 
41235  * 
41236  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41237  * 
41238  * @constructor
41239  * Creates a new Hidden form element.
41240  * @param {Object} config Configuration options
41241  */
41242
41243
41244
41245 // easy hidden field...
41246 Roo.form.Hidden = function(config){
41247     Roo.form.Hidden.superclass.constructor.call(this, config);
41248 };
41249   
41250 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41251     fieldLabel:      '',
41252     inputType:      'hidden',
41253     width:          50,
41254     allowBlank:     true,
41255     labelSeparator: '',
41256     hidden:         true,
41257     itemCls :       'x-form-item-display-none'
41258
41259
41260 });
41261
41262
41263 /*
41264  * Based on:
41265  * Ext JS Library 1.1.1
41266  * Copyright(c) 2006-2007, Ext JS, LLC.
41267  *
41268  * Originally Released Under LGPL - original licence link has changed is not relivant.
41269  *
41270  * Fork - LGPL
41271  * <script type="text/javascript">
41272  */
41273  
41274 /**
41275  * @class Roo.form.TriggerField
41276  * @extends Roo.form.TextField
41277  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41278  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41279  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41280  * for which you can provide a custom implementation.  For example:
41281  * <pre><code>
41282 var trigger = new Roo.form.TriggerField();
41283 trigger.onTriggerClick = myTriggerFn;
41284 trigger.applyTo('my-field');
41285 </code></pre>
41286  *
41287  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41288  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41289  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41290  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41291  * @constructor
41292  * Create a new TriggerField.
41293  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41294  * to the base TextField)
41295  */
41296 Roo.form.TriggerField = function(config){
41297     this.mimicing = false;
41298     Roo.form.TriggerField.superclass.constructor.call(this, config);
41299 };
41300
41301 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41302     /**
41303      * @cfg {String} triggerClass A CSS class to apply to the trigger
41304      */
41305     /**
41306      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41307      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41308      */
41309     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41310     /**
41311      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41312      */
41313     hideTrigger:false,
41314
41315     /** @cfg {Boolean} grow @hide */
41316     /** @cfg {Number} growMin @hide */
41317     /** @cfg {Number} growMax @hide */
41318
41319     /**
41320      * @hide 
41321      * @method
41322      */
41323     autoSize: Roo.emptyFn,
41324     // private
41325     monitorTab : true,
41326     // private
41327     deferHeight : true,
41328
41329     
41330     actionMode : 'wrap',
41331     // private
41332     onResize : function(w, h){
41333         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41334         if(typeof w == 'number'){
41335             var x = w - this.trigger.getWidth();
41336             this.el.setWidth(this.adjustWidth('input', x));
41337             this.trigger.setStyle('left', x+'px');
41338         }
41339     },
41340
41341     // private
41342     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41343
41344     // private
41345     getResizeEl : function(){
41346         return this.wrap;
41347     },
41348
41349     // private
41350     getPositionEl : function(){
41351         return this.wrap;
41352     },
41353
41354     // private
41355     alignErrorIcon : function(){
41356         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41357     },
41358
41359     // private
41360     onRender : function(ct, position){
41361         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41362         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41363         this.trigger = this.wrap.createChild(this.triggerConfig ||
41364                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41365         if(this.hideTrigger){
41366             this.trigger.setDisplayed(false);
41367         }
41368         this.initTrigger();
41369         if(!this.width){
41370             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41371         }
41372     },
41373
41374     // private
41375     initTrigger : function(){
41376         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41377         this.trigger.addClassOnOver('x-form-trigger-over');
41378         this.trigger.addClassOnClick('x-form-trigger-click');
41379     },
41380
41381     // private
41382     onDestroy : function(){
41383         if(this.trigger){
41384             this.trigger.removeAllListeners();
41385             this.trigger.remove();
41386         }
41387         if(this.wrap){
41388             this.wrap.remove();
41389         }
41390         Roo.form.TriggerField.superclass.onDestroy.call(this);
41391     },
41392
41393     // private
41394     onFocus : function(){
41395         Roo.form.TriggerField.superclass.onFocus.call(this);
41396         if(!this.mimicing){
41397             this.wrap.addClass('x-trigger-wrap-focus');
41398             this.mimicing = true;
41399             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41400             if(this.monitorTab){
41401                 this.el.on("keydown", this.checkTab, this);
41402             }
41403         }
41404     },
41405
41406     // private
41407     checkTab : function(e){
41408         if(e.getKey() == e.TAB){
41409             this.triggerBlur();
41410         }
41411     },
41412
41413     // private
41414     onBlur : function(){
41415         // do nothing
41416     },
41417
41418     // private
41419     mimicBlur : function(e, t){
41420         if(!this.wrap.contains(t) && this.validateBlur()){
41421             this.triggerBlur();
41422         }
41423     },
41424
41425     // private
41426     triggerBlur : function(){
41427         this.mimicing = false;
41428         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41429         if(this.monitorTab){
41430             this.el.un("keydown", this.checkTab, this);
41431         }
41432         this.wrap.removeClass('x-trigger-wrap-focus');
41433         Roo.form.TriggerField.superclass.onBlur.call(this);
41434     },
41435
41436     // private
41437     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41438     validateBlur : function(e, t){
41439         return true;
41440     },
41441
41442     // private
41443     onDisable : function(){
41444         Roo.form.TriggerField.superclass.onDisable.call(this);
41445         if(this.wrap){
41446             this.wrap.addClass('x-item-disabled');
41447         }
41448     },
41449
41450     // private
41451     onEnable : function(){
41452         Roo.form.TriggerField.superclass.onEnable.call(this);
41453         if(this.wrap){
41454             this.wrap.removeClass('x-item-disabled');
41455         }
41456     },
41457
41458     // private
41459     onShow : function(){
41460         var ae = this.getActionEl();
41461         
41462         if(ae){
41463             ae.dom.style.display = '';
41464             ae.dom.style.visibility = 'visible';
41465         }
41466     },
41467
41468     // private
41469     
41470     onHide : function(){
41471         var ae = this.getActionEl();
41472         ae.dom.style.display = 'none';
41473     },
41474
41475     /**
41476      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41477      * by an implementing function.
41478      * @method
41479      * @param {EventObject} e
41480      */
41481     onTriggerClick : Roo.emptyFn
41482 });
41483
41484 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41485 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41486 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41487 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41488     initComponent : function(){
41489         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41490
41491         this.triggerConfig = {
41492             tag:'span', cls:'x-form-twin-triggers', cn:[
41493             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41494             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41495         ]};
41496     },
41497
41498     getTrigger : function(index){
41499         return this.triggers[index];
41500     },
41501
41502     initTrigger : function(){
41503         var ts = this.trigger.select('.x-form-trigger', true);
41504         this.wrap.setStyle('overflow', 'hidden');
41505         var triggerField = this;
41506         ts.each(function(t, all, index){
41507             t.hide = function(){
41508                 var w = triggerField.wrap.getWidth();
41509                 this.dom.style.display = 'none';
41510                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41511             };
41512             t.show = function(){
41513                 var w = triggerField.wrap.getWidth();
41514                 this.dom.style.display = '';
41515                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41516             };
41517             var triggerIndex = 'Trigger'+(index+1);
41518
41519             if(this['hide'+triggerIndex]){
41520                 t.dom.style.display = 'none';
41521             }
41522             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41523             t.addClassOnOver('x-form-trigger-over');
41524             t.addClassOnClick('x-form-trigger-click');
41525         }, this);
41526         this.triggers = ts.elements;
41527     },
41528
41529     onTrigger1Click : Roo.emptyFn,
41530     onTrigger2Click : Roo.emptyFn
41531 });/*
41532  * Based on:
41533  * Ext JS Library 1.1.1
41534  * Copyright(c) 2006-2007, Ext JS, LLC.
41535  *
41536  * Originally Released Under LGPL - original licence link has changed is not relivant.
41537  *
41538  * Fork - LGPL
41539  * <script type="text/javascript">
41540  */
41541  
41542 /**
41543  * @class Roo.form.TextArea
41544  * @extends Roo.form.TextField
41545  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41546  * support for auto-sizing.
41547  * @constructor
41548  * Creates a new TextArea
41549  * @param {Object} config Configuration options
41550  */
41551 Roo.form.TextArea = function(config){
41552     Roo.form.TextArea.superclass.constructor.call(this, config);
41553     // these are provided exchanges for backwards compat
41554     // minHeight/maxHeight were replaced by growMin/growMax to be
41555     // compatible with TextField growing config values
41556     if(this.minHeight !== undefined){
41557         this.growMin = this.minHeight;
41558     }
41559     if(this.maxHeight !== undefined){
41560         this.growMax = this.maxHeight;
41561     }
41562 };
41563
41564 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41565     /**
41566      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41567      */
41568     growMin : 60,
41569     /**
41570      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41571      */
41572     growMax: 1000,
41573     /**
41574      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41575      * in the field (equivalent to setting overflow: hidden, defaults to false)
41576      */
41577     preventScrollbars: false,
41578     /**
41579      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41580      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41581      */
41582
41583     // private
41584     onRender : function(ct, position){
41585         if(!this.el){
41586             this.defaultAutoCreate = {
41587                 tag: "textarea",
41588                 style:"width:300px;height:60px;",
41589                 autocomplete: "new-password"
41590             };
41591         }
41592         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41593         if(this.grow){
41594             this.textSizeEl = Roo.DomHelper.append(document.body, {
41595                 tag: "pre", cls: "x-form-grow-sizer"
41596             });
41597             if(this.preventScrollbars){
41598                 this.el.setStyle("overflow", "hidden");
41599             }
41600             this.el.setHeight(this.growMin);
41601         }
41602     },
41603
41604     onDestroy : function(){
41605         if(this.textSizeEl){
41606             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41607         }
41608         Roo.form.TextArea.superclass.onDestroy.call(this);
41609     },
41610
41611     // private
41612     onKeyUp : function(e){
41613         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41614             this.autoSize();
41615         }
41616     },
41617
41618     /**
41619      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41620      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41621      */
41622     autoSize : function(){
41623         if(!this.grow || !this.textSizeEl){
41624             return;
41625         }
41626         var el = this.el;
41627         var v = el.dom.value;
41628         var ts = this.textSizeEl;
41629
41630         ts.innerHTML = '';
41631         ts.appendChild(document.createTextNode(v));
41632         v = ts.innerHTML;
41633
41634         Roo.fly(ts).setWidth(this.el.getWidth());
41635         if(v.length < 1){
41636             v = "&#160;&#160;";
41637         }else{
41638             if(Roo.isIE){
41639                 v = v.replace(/\n/g, '<p>&#160;</p>');
41640             }
41641             v += "&#160;\n&#160;";
41642         }
41643         ts.innerHTML = v;
41644         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41645         if(h != this.lastHeight){
41646             this.lastHeight = h;
41647             this.el.setHeight(h);
41648             this.fireEvent("autosize", this, h);
41649         }
41650     }
41651 });/*
41652  * Based on:
41653  * Ext JS Library 1.1.1
41654  * Copyright(c) 2006-2007, Ext JS, LLC.
41655  *
41656  * Originally Released Under LGPL - original licence link has changed is not relivant.
41657  *
41658  * Fork - LGPL
41659  * <script type="text/javascript">
41660  */
41661  
41662
41663 /**
41664  * @class Roo.form.NumberField
41665  * @extends Roo.form.TextField
41666  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41667  * @constructor
41668  * Creates a new NumberField
41669  * @param {Object} config Configuration options
41670  */
41671 Roo.form.NumberField = function(config){
41672     Roo.form.NumberField.superclass.constructor.call(this, config);
41673 };
41674
41675 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41676     /**
41677      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41678      */
41679     fieldClass: "x-form-field x-form-num-field",
41680     /**
41681      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41682      */
41683     allowDecimals : true,
41684     /**
41685      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41686      */
41687     decimalSeparator : ".",
41688     /**
41689      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41690      */
41691     decimalPrecision : 2,
41692     /**
41693      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41694      */
41695     allowNegative : true,
41696     /**
41697      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41698      */
41699     minValue : Number.NEGATIVE_INFINITY,
41700     /**
41701      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41702      */
41703     maxValue : Number.MAX_VALUE,
41704     /**
41705      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41706      */
41707     minText : "The minimum value for this field is {0}",
41708     /**
41709      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41710      */
41711     maxText : "The maximum value for this field is {0}",
41712     /**
41713      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41714      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41715      */
41716     nanText : "{0} is not a valid number",
41717
41718     // private
41719     initEvents : function(){
41720         Roo.form.NumberField.superclass.initEvents.call(this);
41721         var allowed = "0123456789";
41722         if(this.allowDecimals){
41723             allowed += this.decimalSeparator;
41724         }
41725         if(this.allowNegative){
41726             allowed += "-";
41727         }
41728         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41729         var keyPress = function(e){
41730             var k = e.getKey();
41731             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41732                 return;
41733             }
41734             var c = e.getCharCode();
41735             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41736                 e.stopEvent();
41737             }
41738         };
41739         this.el.on("keypress", keyPress, this);
41740     },
41741
41742     // private
41743     validateValue : function(value){
41744         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41745             return false;
41746         }
41747         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41748              return true;
41749         }
41750         var num = this.parseValue(value);
41751         if(isNaN(num)){
41752             this.markInvalid(String.format(this.nanText, value));
41753             return false;
41754         }
41755         if(num < this.minValue){
41756             this.markInvalid(String.format(this.minText, this.minValue));
41757             return false;
41758         }
41759         if(num > this.maxValue){
41760             this.markInvalid(String.format(this.maxText, this.maxValue));
41761             return false;
41762         }
41763         return true;
41764     },
41765
41766     getValue : function(){
41767         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41768     },
41769
41770     // private
41771     parseValue : function(value){
41772         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41773         return isNaN(value) ? '' : value;
41774     },
41775
41776     // private
41777     fixPrecision : function(value){
41778         var nan = isNaN(value);
41779         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41780             return nan ? '' : value;
41781         }
41782         return parseFloat(value).toFixed(this.decimalPrecision);
41783     },
41784
41785     setValue : function(v){
41786         v = this.fixPrecision(v);
41787         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41788     },
41789
41790     // private
41791     decimalPrecisionFcn : function(v){
41792         return Math.floor(v);
41793     },
41794
41795     beforeBlur : function(){
41796         var v = this.parseValue(this.getRawValue());
41797         if(v){
41798             this.setValue(v);
41799         }
41800     }
41801 });/*
41802  * Based on:
41803  * Ext JS Library 1.1.1
41804  * Copyright(c) 2006-2007, Ext JS, LLC.
41805  *
41806  * Originally Released Under LGPL - original licence link has changed is not relivant.
41807  *
41808  * Fork - LGPL
41809  * <script type="text/javascript">
41810  */
41811  
41812 /**
41813  * @class Roo.form.DateField
41814  * @extends Roo.form.TriggerField
41815  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41816 * @constructor
41817 * Create a new DateField
41818 * @param {Object} config
41819  */
41820 Roo.form.DateField = function(config)
41821 {
41822     Roo.form.DateField.superclass.constructor.call(this, config);
41823     
41824       this.addEvents({
41825          
41826         /**
41827          * @event select
41828          * Fires when a date is selected
41829              * @param {Roo.form.DateField} combo This combo box
41830              * @param {Date} date The date selected
41831              */
41832         'select' : true
41833          
41834     });
41835     
41836     
41837     if(typeof this.minValue == "string") {
41838         this.minValue = this.parseDate(this.minValue);
41839     }
41840     if(typeof this.maxValue == "string") {
41841         this.maxValue = this.parseDate(this.maxValue);
41842     }
41843     this.ddMatch = null;
41844     if(this.disabledDates){
41845         var dd = this.disabledDates;
41846         var re = "(?:";
41847         for(var i = 0; i < dd.length; i++){
41848             re += dd[i];
41849             if(i != dd.length-1) {
41850                 re += "|";
41851             }
41852         }
41853         this.ddMatch = new RegExp(re + ")");
41854     }
41855 };
41856
41857 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41858     /**
41859      * @cfg {String} format
41860      * The default date format string which can be overriden for localization support.  The format must be
41861      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41862      */
41863     format : "m/d/y",
41864     /**
41865      * @cfg {String} altFormats
41866      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41867      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41868      */
41869     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41870     /**
41871      * @cfg {Array} disabledDays
41872      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41873      */
41874     disabledDays : null,
41875     /**
41876      * @cfg {String} disabledDaysText
41877      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41878      */
41879     disabledDaysText : "Disabled",
41880     /**
41881      * @cfg {Array} disabledDates
41882      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41883      * expression so they are very powerful. Some examples:
41884      * <ul>
41885      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41886      * <li>["03/08", "09/16"] would disable those days for every year</li>
41887      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41888      * <li>["03/../2006"] would disable every day in March 2006</li>
41889      * <li>["^03"] would disable every day in every March</li>
41890      * </ul>
41891      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41892      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41893      */
41894     disabledDates : null,
41895     /**
41896      * @cfg {String} disabledDatesText
41897      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41898      */
41899     disabledDatesText : "Disabled",
41900         
41901         
41902         /**
41903      * @cfg {Date/String} zeroValue
41904      * if the date is less that this number, then the field is rendered as empty
41905      * default is 1800
41906      */
41907         zeroValue : '1800-01-01',
41908         
41909         
41910     /**
41911      * @cfg {Date/String} minValue
41912      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41913      * valid format (defaults to null).
41914      */
41915     minValue : null,
41916     /**
41917      * @cfg {Date/String} maxValue
41918      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41919      * valid format (defaults to null).
41920      */
41921     maxValue : null,
41922     /**
41923      * @cfg {String} minText
41924      * The error text to display when the date in the cell is before minValue (defaults to
41925      * 'The date in this field must be after {minValue}').
41926      */
41927     minText : "The date in this field must be equal to or after {0}",
41928     /**
41929      * @cfg {String} maxText
41930      * The error text to display when the date in the cell is after maxValue (defaults to
41931      * 'The date in this field must be before {maxValue}').
41932      */
41933     maxText : "The date in this field must be equal to or before {0}",
41934     /**
41935      * @cfg {String} invalidText
41936      * The error text to display when the date in the field is invalid (defaults to
41937      * '{value} is not a valid date - it must be in the format {format}').
41938      */
41939     invalidText : "{0} is not a valid date - it must be in the format {1}",
41940     /**
41941      * @cfg {String} triggerClass
41942      * An additional CSS class used to style the trigger button.  The trigger will always get the
41943      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41944      * which displays a calendar icon).
41945      */
41946     triggerClass : 'x-form-date-trigger',
41947     
41948
41949     /**
41950      * @cfg {Boolean} useIso
41951      * if enabled, then the date field will use a hidden field to store the 
41952      * real value as iso formated date. default (false)
41953      */ 
41954     useIso : false,
41955     /**
41956      * @cfg {String/Object} autoCreate
41957      * A DomHelper element spec, or true for a default element spec (defaults to
41958      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41959      */ 
41960     // private
41961     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41962     
41963     // private
41964     hiddenField: false,
41965     
41966     onRender : function(ct, position)
41967     {
41968         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41969         if (this.useIso) {
41970             //this.el.dom.removeAttribute('name'); 
41971             Roo.log("Changing name?");
41972             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41973             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41974                     'before', true);
41975             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41976             // prevent input submission
41977             this.hiddenName = this.name;
41978         }
41979             
41980             
41981     },
41982     
41983     // private
41984     validateValue : function(value)
41985     {
41986         value = this.formatDate(value);
41987         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41988             Roo.log('super failed');
41989             return false;
41990         }
41991         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41992              return true;
41993         }
41994         var svalue = value;
41995         value = this.parseDate(value);
41996         if(!value){
41997             Roo.log('parse date failed' + svalue);
41998             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41999             return false;
42000         }
42001         var time = value.getTime();
42002         if(this.minValue && time < this.minValue.getTime()){
42003             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42004             return false;
42005         }
42006         if(this.maxValue && time > this.maxValue.getTime()){
42007             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42008             return false;
42009         }
42010         if(this.disabledDays){
42011             var day = value.getDay();
42012             for(var i = 0; i < this.disabledDays.length; i++) {
42013                 if(day === this.disabledDays[i]){
42014                     this.markInvalid(this.disabledDaysText);
42015                     return false;
42016                 }
42017             }
42018         }
42019         var fvalue = this.formatDate(value);
42020         if(this.ddMatch && this.ddMatch.test(fvalue)){
42021             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42022             return false;
42023         }
42024         return true;
42025     },
42026
42027     // private
42028     // Provides logic to override the default TriggerField.validateBlur which just returns true
42029     validateBlur : function(){
42030         return !this.menu || !this.menu.isVisible();
42031     },
42032     
42033     getName: function()
42034     {
42035         // returns hidden if it's set..
42036         if (!this.rendered) {return ''};
42037         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42038         
42039     },
42040
42041     /**
42042      * Returns the current date value of the date field.
42043      * @return {Date} The date value
42044      */
42045     getValue : function(){
42046         
42047         return  this.hiddenField ?
42048                 this.hiddenField.value :
42049                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42050     },
42051
42052     /**
42053      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42054      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42055      * (the default format used is "m/d/y").
42056      * <br />Usage:
42057      * <pre><code>
42058 //All of these calls set the same date value (May 4, 2006)
42059
42060 //Pass a date object:
42061 var dt = new Date('5/4/06');
42062 dateField.setValue(dt);
42063
42064 //Pass a date string (default format):
42065 dateField.setValue('5/4/06');
42066
42067 //Pass a date string (custom format):
42068 dateField.format = 'Y-m-d';
42069 dateField.setValue('2006-5-4');
42070 </code></pre>
42071      * @param {String/Date} date The date or valid date string
42072      */
42073     setValue : function(date){
42074         if (this.hiddenField) {
42075             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42076         }
42077         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42078         // make sure the value field is always stored as a date..
42079         this.value = this.parseDate(date);
42080         
42081         
42082     },
42083
42084     // private
42085     parseDate : function(value){
42086                 
42087                 if (value instanceof Date) {
42088                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42089                                 return  '';
42090                         }
42091                         return value;
42092                 }
42093                 
42094                 
42095         if(!value || value instanceof Date){
42096             return value;
42097         }
42098         var v = Date.parseDate(value, this.format);
42099          if (!v && this.useIso) {
42100             v = Date.parseDate(value, 'Y-m-d');
42101         }
42102         if(!v && this.altFormats){
42103             if(!this.altFormatsArray){
42104                 this.altFormatsArray = this.altFormats.split("|");
42105             }
42106             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42107                 v = Date.parseDate(value, this.altFormatsArray[i]);
42108             }
42109         }
42110                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42111                         v = '';
42112                 }
42113         return v;
42114     },
42115
42116     // private
42117     formatDate : function(date, fmt){
42118         return (!date || !(date instanceof Date)) ?
42119                date : date.dateFormat(fmt || this.format);
42120     },
42121
42122     // private
42123     menuListeners : {
42124         select: function(m, d){
42125             
42126             this.setValue(d);
42127             this.fireEvent('select', this, d);
42128         },
42129         show : function(){ // retain focus styling
42130             this.onFocus();
42131         },
42132         hide : function(){
42133             this.focus.defer(10, this);
42134             var ml = this.menuListeners;
42135             this.menu.un("select", ml.select,  this);
42136             this.menu.un("show", ml.show,  this);
42137             this.menu.un("hide", ml.hide,  this);
42138         }
42139     },
42140
42141     // private
42142     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42143     onTriggerClick : function(){
42144         if(this.disabled){
42145             return;
42146         }
42147         if(this.menu == null){
42148             this.menu = new Roo.menu.DateMenu();
42149         }
42150         Roo.apply(this.menu.picker,  {
42151             showClear: this.allowBlank,
42152             minDate : this.minValue,
42153             maxDate : this.maxValue,
42154             disabledDatesRE : this.ddMatch,
42155             disabledDatesText : this.disabledDatesText,
42156             disabledDays : this.disabledDays,
42157             disabledDaysText : this.disabledDaysText,
42158             format : this.useIso ? 'Y-m-d' : this.format,
42159             minText : String.format(this.minText, this.formatDate(this.minValue)),
42160             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42161         });
42162         this.menu.on(Roo.apply({}, this.menuListeners, {
42163             scope:this
42164         }));
42165         this.menu.picker.setValue(this.getValue() || new Date());
42166         this.menu.show(this.el, "tl-bl?");
42167     },
42168
42169     beforeBlur : function(){
42170         var v = this.parseDate(this.getRawValue());
42171         if(v){
42172             this.setValue(v);
42173         }
42174     },
42175
42176     /*@
42177      * overide
42178      * 
42179      */
42180     isDirty : function() {
42181         if(this.disabled) {
42182             return false;
42183         }
42184         
42185         if(typeof(this.startValue) === 'undefined'){
42186             return false;
42187         }
42188         
42189         return String(this.getValue()) !== String(this.startValue);
42190         
42191     },
42192     // @overide
42193     cleanLeadingSpace : function(e)
42194     {
42195        return;
42196     }
42197     
42198 });/*
42199  * Based on:
42200  * Ext JS Library 1.1.1
42201  * Copyright(c) 2006-2007, Ext JS, LLC.
42202  *
42203  * Originally Released Under LGPL - original licence link has changed is not relivant.
42204  *
42205  * Fork - LGPL
42206  * <script type="text/javascript">
42207  */
42208  
42209 /**
42210  * @class Roo.form.MonthField
42211  * @extends Roo.form.TriggerField
42212  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42213 * @constructor
42214 * Create a new MonthField
42215 * @param {Object} config
42216  */
42217 Roo.form.MonthField = function(config){
42218     
42219     Roo.form.MonthField.superclass.constructor.call(this, config);
42220     
42221       this.addEvents({
42222          
42223         /**
42224          * @event select
42225          * Fires when a date is selected
42226              * @param {Roo.form.MonthFieeld} combo This combo box
42227              * @param {Date} date The date selected
42228              */
42229         'select' : true
42230          
42231     });
42232     
42233     
42234     if(typeof this.minValue == "string") {
42235         this.minValue = this.parseDate(this.minValue);
42236     }
42237     if(typeof this.maxValue == "string") {
42238         this.maxValue = this.parseDate(this.maxValue);
42239     }
42240     this.ddMatch = null;
42241     if(this.disabledDates){
42242         var dd = this.disabledDates;
42243         var re = "(?:";
42244         for(var i = 0; i < dd.length; i++){
42245             re += dd[i];
42246             if(i != dd.length-1) {
42247                 re += "|";
42248             }
42249         }
42250         this.ddMatch = new RegExp(re + ")");
42251     }
42252 };
42253
42254 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42255     /**
42256      * @cfg {String} format
42257      * The default date format string which can be overriden for localization support.  The format must be
42258      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42259      */
42260     format : "M Y",
42261     /**
42262      * @cfg {String} altFormats
42263      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42264      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42265      */
42266     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42267     /**
42268      * @cfg {Array} disabledDays
42269      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42270      */
42271     disabledDays : [0,1,2,3,4,5,6],
42272     /**
42273      * @cfg {String} disabledDaysText
42274      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42275      */
42276     disabledDaysText : "Disabled",
42277     /**
42278      * @cfg {Array} disabledDates
42279      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42280      * expression so they are very powerful. Some examples:
42281      * <ul>
42282      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42283      * <li>["03/08", "09/16"] would disable those days for every year</li>
42284      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42285      * <li>["03/../2006"] would disable every day in March 2006</li>
42286      * <li>["^03"] would disable every day in every March</li>
42287      * </ul>
42288      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42289      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42290      */
42291     disabledDates : null,
42292     /**
42293      * @cfg {String} disabledDatesText
42294      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42295      */
42296     disabledDatesText : "Disabled",
42297     /**
42298      * @cfg {Date/String} minValue
42299      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42300      * valid format (defaults to null).
42301      */
42302     minValue : null,
42303     /**
42304      * @cfg {Date/String} maxValue
42305      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42306      * valid format (defaults to null).
42307      */
42308     maxValue : null,
42309     /**
42310      * @cfg {String} minText
42311      * The error text to display when the date in the cell is before minValue (defaults to
42312      * 'The date in this field must be after {minValue}').
42313      */
42314     minText : "The date in this field must be equal to or after {0}",
42315     /**
42316      * @cfg {String} maxTextf
42317      * The error text to display when the date in the cell is after maxValue (defaults to
42318      * 'The date in this field must be before {maxValue}').
42319      */
42320     maxText : "The date in this field must be equal to or before {0}",
42321     /**
42322      * @cfg {String} invalidText
42323      * The error text to display when the date in the field is invalid (defaults to
42324      * '{value} is not a valid date - it must be in the format {format}').
42325      */
42326     invalidText : "{0} is not a valid date - it must be in the format {1}",
42327     /**
42328      * @cfg {String} triggerClass
42329      * 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-date-trigger'
42331      * which displays a calendar icon).
42332      */
42333     triggerClass : 'x-form-date-trigger',
42334     
42335
42336     /**
42337      * @cfg {Boolean} useIso
42338      * if enabled, then the date field will use a hidden field to store the 
42339      * real value as iso formated date. default (true)
42340      */ 
42341     useIso : true,
42342     /**
42343      * @cfg {String/Object} autoCreate
42344      * A DomHelper element spec, or true for a default element spec (defaults to
42345      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42346      */ 
42347     // private
42348     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42349     
42350     // private
42351     hiddenField: false,
42352     
42353     hideMonthPicker : false,
42354     
42355     onRender : function(ct, position)
42356     {
42357         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42358         if (this.useIso) {
42359             this.el.dom.removeAttribute('name'); 
42360             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42361                     'before', true);
42362             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42363             // prevent input submission
42364             this.hiddenName = this.name;
42365         }
42366             
42367             
42368     },
42369     
42370     // private
42371     validateValue : function(value)
42372     {
42373         value = this.formatDate(value);
42374         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42375             return false;
42376         }
42377         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42378              return true;
42379         }
42380         var svalue = value;
42381         value = this.parseDate(value);
42382         if(!value){
42383             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42384             return false;
42385         }
42386         var time = value.getTime();
42387         if(this.minValue && time < this.minValue.getTime()){
42388             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42389             return false;
42390         }
42391         if(this.maxValue && time > this.maxValue.getTime()){
42392             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42393             return false;
42394         }
42395         /*if(this.disabledDays){
42396             var day = value.getDay();
42397             for(var i = 0; i < this.disabledDays.length; i++) {
42398                 if(day === this.disabledDays[i]){
42399                     this.markInvalid(this.disabledDaysText);
42400                     return false;
42401                 }
42402             }
42403         }
42404         */
42405         var fvalue = this.formatDate(value);
42406         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42407             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42408             return false;
42409         }
42410         */
42411         return true;
42412     },
42413
42414     // private
42415     // Provides logic to override the default TriggerField.validateBlur which just returns true
42416     validateBlur : function(){
42417         return !this.menu || !this.menu.isVisible();
42418     },
42419
42420     /**
42421      * Returns the current date value of the date field.
42422      * @return {Date} The date value
42423      */
42424     getValue : function(){
42425         
42426         
42427         
42428         return  this.hiddenField ?
42429                 this.hiddenField.value :
42430                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42431     },
42432
42433     /**
42434      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42435      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42436      * (the default format used is "m/d/y").
42437      * <br />Usage:
42438      * <pre><code>
42439 //All of these calls set the same date value (May 4, 2006)
42440
42441 //Pass a date object:
42442 var dt = new Date('5/4/06');
42443 monthField.setValue(dt);
42444
42445 //Pass a date string (default format):
42446 monthField.setValue('5/4/06');
42447
42448 //Pass a date string (custom format):
42449 monthField.format = 'Y-m-d';
42450 monthField.setValue('2006-5-4');
42451 </code></pre>
42452      * @param {String/Date} date The date or valid date string
42453      */
42454     setValue : function(date){
42455         Roo.log('month setValue' + date);
42456         // can only be first of month..
42457         
42458         var val = this.parseDate(date);
42459         
42460         if (this.hiddenField) {
42461             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42462         }
42463         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42464         this.value = this.parseDate(date);
42465     },
42466
42467     // private
42468     parseDate : function(value){
42469         if(!value || value instanceof Date){
42470             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42471             return value;
42472         }
42473         var v = Date.parseDate(value, this.format);
42474         if (!v && this.useIso) {
42475             v = Date.parseDate(value, 'Y-m-d');
42476         }
42477         if (v) {
42478             // 
42479             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42480         }
42481         
42482         
42483         if(!v && this.altFormats){
42484             if(!this.altFormatsArray){
42485                 this.altFormatsArray = this.altFormats.split("|");
42486             }
42487             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42488                 v = Date.parseDate(value, this.altFormatsArray[i]);
42489             }
42490         }
42491         return v;
42492     },
42493
42494     // private
42495     formatDate : function(date, fmt){
42496         return (!date || !(date instanceof Date)) ?
42497                date : date.dateFormat(fmt || this.format);
42498     },
42499
42500     // private
42501     menuListeners : {
42502         select: function(m, d){
42503             this.setValue(d);
42504             this.fireEvent('select', this, d);
42505         },
42506         show : function(){ // retain focus styling
42507             this.onFocus();
42508         },
42509         hide : function(){
42510             this.focus.defer(10, this);
42511             var ml = this.menuListeners;
42512             this.menu.un("select", ml.select,  this);
42513             this.menu.un("show", ml.show,  this);
42514             this.menu.un("hide", ml.hide,  this);
42515         }
42516     },
42517     // private
42518     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42519     onTriggerClick : function(){
42520         if(this.disabled){
42521             return;
42522         }
42523         if(this.menu == null){
42524             this.menu = new Roo.menu.DateMenu();
42525            
42526         }
42527         
42528         Roo.apply(this.menu.picker,  {
42529             
42530             showClear: this.allowBlank,
42531             minDate : this.minValue,
42532             maxDate : this.maxValue,
42533             disabledDatesRE : this.ddMatch,
42534             disabledDatesText : this.disabledDatesText,
42535             
42536             format : this.useIso ? 'Y-m-d' : this.format,
42537             minText : String.format(this.minText, this.formatDate(this.minValue)),
42538             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42539             
42540         });
42541          this.menu.on(Roo.apply({}, this.menuListeners, {
42542             scope:this
42543         }));
42544        
42545         
42546         var m = this.menu;
42547         var p = m.picker;
42548         
42549         // hide month picker get's called when we called by 'before hide';
42550         
42551         var ignorehide = true;
42552         p.hideMonthPicker  = function(disableAnim){
42553             if (ignorehide) {
42554                 return;
42555             }
42556              if(this.monthPicker){
42557                 Roo.log("hideMonthPicker called");
42558                 if(disableAnim === true){
42559                     this.monthPicker.hide();
42560                 }else{
42561                     this.monthPicker.slideOut('t', {duration:.2});
42562                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42563                     p.fireEvent("select", this, this.value);
42564                     m.hide();
42565                 }
42566             }
42567         }
42568         
42569         Roo.log('picker set value');
42570         Roo.log(this.getValue());
42571         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42572         m.show(this.el, 'tl-bl?');
42573         ignorehide  = false;
42574         // this will trigger hideMonthPicker..
42575         
42576         
42577         // hidden the day picker
42578         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42579         
42580         
42581         
42582       
42583         
42584         p.showMonthPicker.defer(100, p);
42585     
42586         
42587        
42588     },
42589
42590     beforeBlur : function(){
42591         var v = this.parseDate(this.getRawValue());
42592         if(v){
42593             this.setValue(v);
42594         }
42595     }
42596
42597     /** @cfg {Boolean} grow @hide */
42598     /** @cfg {Number} growMin @hide */
42599     /** @cfg {Number} growMax @hide */
42600     /**
42601      * @hide
42602      * @method autoSize
42603      */
42604 });/*
42605  * Based on:
42606  * Ext JS Library 1.1.1
42607  * Copyright(c) 2006-2007, Ext JS, LLC.
42608  *
42609  * Originally Released Under LGPL - original licence link has changed is not relivant.
42610  *
42611  * Fork - LGPL
42612  * <script type="text/javascript">
42613  */
42614  
42615
42616 /**
42617  * @class Roo.form.ComboBox
42618  * @extends Roo.form.TriggerField
42619  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42620  * @constructor
42621  * Create a new ComboBox.
42622  * @param {Object} config Configuration options
42623  */
42624 Roo.form.ComboBox = function(config){
42625     Roo.form.ComboBox.superclass.constructor.call(this, config);
42626     this.addEvents({
42627         /**
42628          * @event expand
42629          * Fires when the dropdown list is expanded
42630              * @param {Roo.form.ComboBox} combo This combo box
42631              */
42632         'expand' : true,
42633         /**
42634          * @event collapse
42635          * Fires when the dropdown list is collapsed
42636              * @param {Roo.form.ComboBox} combo This combo box
42637              */
42638         'collapse' : true,
42639         /**
42640          * @event beforeselect
42641          * Fires before a list item is selected. Return false to cancel the selection.
42642              * @param {Roo.form.ComboBox} combo This combo box
42643              * @param {Roo.data.Record} record The data record returned from the underlying store
42644              * @param {Number} index The index of the selected item in the dropdown list
42645              */
42646         'beforeselect' : true,
42647         /**
42648          * @event select
42649          * Fires when a list item is selected
42650              * @param {Roo.form.ComboBox} combo This combo box
42651              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42652              * @param {Number} index The index of the selected item in the dropdown list
42653              */
42654         'select' : true,
42655         /**
42656          * @event beforequery
42657          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42658          * The event object passed has these properties:
42659              * @param {Roo.form.ComboBox} combo This combo box
42660              * @param {String} query The query
42661              * @param {Boolean} forceAll true to force "all" query
42662              * @param {Boolean} cancel true to cancel the query
42663              * @param {Object} e The query event object
42664              */
42665         'beforequery': true,
42666          /**
42667          * @event add
42668          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42669              * @param {Roo.form.ComboBox} combo This combo box
42670              */
42671         'add' : true,
42672         /**
42673          * @event edit
42674          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42675              * @param {Roo.form.ComboBox} combo This combo box
42676              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42677              */
42678         'edit' : true
42679         
42680         
42681     });
42682     if(this.transform){
42683         this.allowDomMove = false;
42684         var s = Roo.getDom(this.transform);
42685         if(!this.hiddenName){
42686             this.hiddenName = s.name;
42687         }
42688         if(!this.store){
42689             this.mode = 'local';
42690             var d = [], opts = s.options;
42691             for(var i = 0, len = opts.length;i < len; i++){
42692                 var o = opts[i];
42693                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42694                 if(o.selected) {
42695                     this.value = value;
42696                 }
42697                 d.push([value, o.text]);
42698             }
42699             this.store = new Roo.data.SimpleStore({
42700                 'id': 0,
42701                 fields: ['value', 'text'],
42702                 data : d
42703             });
42704             this.valueField = 'value';
42705             this.displayField = 'text';
42706         }
42707         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42708         if(!this.lazyRender){
42709             this.target = true;
42710             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42711             s.parentNode.removeChild(s); // remove it
42712             this.render(this.el.parentNode);
42713         }else{
42714             s.parentNode.removeChild(s); // remove it
42715         }
42716
42717     }
42718     if (this.store) {
42719         this.store = Roo.factory(this.store, Roo.data);
42720     }
42721     
42722     this.selectedIndex = -1;
42723     if(this.mode == 'local'){
42724         if(config.queryDelay === undefined){
42725             this.queryDelay = 10;
42726         }
42727         if(config.minChars === undefined){
42728             this.minChars = 0;
42729         }
42730     }
42731 };
42732
42733 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42734     /**
42735      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42736      */
42737     /**
42738      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42739      * rendering into an Roo.Editor, defaults to false)
42740      */
42741     /**
42742      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42743      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42744      */
42745     /**
42746      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42747      */
42748     /**
42749      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42750      * the dropdown list (defaults to undefined, with no header element)
42751      */
42752
42753      /**
42754      * @cfg {String/Roo.Template} tpl The template to use to render the output
42755      */
42756      
42757     // private
42758     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42759     /**
42760      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42761      */
42762     listWidth: undefined,
42763     /**
42764      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42765      * mode = 'remote' or 'text' if mode = 'local')
42766      */
42767     displayField: undefined,
42768     /**
42769      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42770      * mode = 'remote' or 'value' if mode = 'local'). 
42771      * Note: use of a valueField requires the user make a selection
42772      * in order for a value to be mapped.
42773      */
42774     valueField: undefined,
42775     
42776     
42777     /**
42778      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42779      * field's data value (defaults to the underlying DOM element's name)
42780      */
42781     hiddenName: undefined,
42782     /**
42783      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42784      */
42785     listClass: '',
42786     /**
42787      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42788      */
42789     selectedClass: 'x-combo-selected',
42790     /**
42791      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42792      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42793      * which displays a downward arrow icon).
42794      */
42795     triggerClass : 'x-form-arrow-trigger',
42796     /**
42797      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42798      */
42799     shadow:'sides',
42800     /**
42801      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42802      * anchor positions (defaults to 'tl-bl')
42803      */
42804     listAlign: 'tl-bl?',
42805     /**
42806      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42807      */
42808     maxHeight: 300,
42809     /**
42810      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42811      * query specified by the allQuery config option (defaults to 'query')
42812      */
42813     triggerAction: 'query',
42814     /**
42815      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42816      * (defaults to 4, does not apply if editable = false)
42817      */
42818     minChars : 4,
42819     /**
42820      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42821      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42822      */
42823     typeAhead: false,
42824     /**
42825      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42826      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42827      */
42828     queryDelay: 500,
42829     /**
42830      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42831      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42832      */
42833     pageSize: 0,
42834     /**
42835      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42836      * when editable = true (defaults to false)
42837      */
42838     selectOnFocus:false,
42839     /**
42840      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42841      */
42842     queryParam: 'query',
42843     /**
42844      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42845      * when mode = 'remote' (defaults to 'Loading...')
42846      */
42847     loadingText: 'Loading...',
42848     /**
42849      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42850      */
42851     resizable: false,
42852     /**
42853      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42854      */
42855     handleHeight : 8,
42856     /**
42857      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42858      * traditional select (defaults to true)
42859      */
42860     editable: true,
42861     /**
42862      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42863      */
42864     allQuery: '',
42865     /**
42866      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42867      */
42868     mode: 'remote',
42869     /**
42870      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42871      * listWidth has a higher value)
42872      */
42873     minListWidth : 70,
42874     /**
42875      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42876      * allow the user to set arbitrary text into the field (defaults to false)
42877      */
42878     forceSelection:false,
42879     /**
42880      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42881      * if typeAhead = true (defaults to 250)
42882      */
42883     typeAheadDelay : 250,
42884     /**
42885      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42886      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42887      */
42888     valueNotFoundText : undefined,
42889     /**
42890      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42891      */
42892     blockFocus : false,
42893     
42894     /**
42895      * @cfg {Boolean} disableClear Disable showing of clear button.
42896      */
42897     disableClear : false,
42898     /**
42899      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42900      */
42901     alwaysQuery : false,
42902     
42903     //private
42904     addicon : false,
42905     editicon: false,
42906     
42907     // element that contains real text value.. (when hidden is used..)
42908      
42909     // private
42910     onRender : function(ct, position)
42911     {
42912         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42913         
42914         if(this.hiddenName){
42915             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42916                     'before', true);
42917             this.hiddenField.value =
42918                 this.hiddenValue !== undefined ? this.hiddenValue :
42919                 this.value !== undefined ? this.value : '';
42920
42921             // prevent input submission
42922             this.el.dom.removeAttribute('name');
42923              
42924              
42925         }
42926         
42927         if(Roo.isGecko){
42928             this.el.dom.setAttribute('autocomplete', 'off');
42929         }
42930
42931         var cls = 'x-combo-list';
42932
42933         this.list = new Roo.Layer({
42934             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42935         });
42936
42937         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42938         this.list.setWidth(lw);
42939         this.list.swallowEvent('mousewheel');
42940         this.assetHeight = 0;
42941
42942         if(this.title){
42943             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42944             this.assetHeight += this.header.getHeight();
42945         }
42946
42947         this.innerList = this.list.createChild({cls:cls+'-inner'});
42948         this.innerList.on('mouseover', this.onViewOver, this);
42949         this.innerList.on('mousemove', this.onViewMove, this);
42950         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42951         
42952         if(this.allowBlank && !this.pageSize && !this.disableClear){
42953             this.footer = this.list.createChild({cls:cls+'-ft'});
42954             this.pageTb = new Roo.Toolbar(this.footer);
42955            
42956         }
42957         if(this.pageSize){
42958             this.footer = this.list.createChild({cls:cls+'-ft'});
42959             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42960                     {pageSize: this.pageSize});
42961             
42962         }
42963         
42964         if (this.pageTb && this.allowBlank && !this.disableClear) {
42965             var _this = this;
42966             this.pageTb.add(new Roo.Toolbar.Fill(), {
42967                 cls: 'x-btn-icon x-btn-clear',
42968                 text: '&#160;',
42969                 handler: function()
42970                 {
42971                     _this.collapse();
42972                     _this.clearValue();
42973                     _this.onSelect(false, -1);
42974                 }
42975             });
42976         }
42977         if (this.footer) {
42978             this.assetHeight += this.footer.getHeight();
42979         }
42980         
42981
42982         if(!this.tpl){
42983             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42984         }
42985
42986         this.view = new Roo.View(this.innerList, this.tpl, {
42987             singleSelect:true,
42988             store: this.store,
42989             selectedClass: this.selectedClass
42990         });
42991
42992         this.view.on('click', this.onViewClick, this);
42993
42994         this.store.on('beforeload', this.onBeforeLoad, this);
42995         this.store.on('load', this.onLoad, this);
42996         this.store.on('loadexception', this.onLoadException, this);
42997
42998         if(this.resizable){
42999             this.resizer = new Roo.Resizable(this.list,  {
43000                pinned:true, handles:'se'
43001             });
43002             this.resizer.on('resize', function(r, w, h){
43003                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
43004                 this.listWidth = w;
43005                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
43006                 this.restrictHeight();
43007             }, this);
43008             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
43009         }
43010         if(!this.editable){
43011             this.editable = true;
43012             this.setEditable(false);
43013         }  
43014         
43015         
43016         if (typeof(this.events.add.listeners) != 'undefined') {
43017             
43018             this.addicon = this.wrap.createChild(
43019                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43020        
43021             this.addicon.on('click', function(e) {
43022                 this.fireEvent('add', this);
43023             }, this);
43024         }
43025         if (typeof(this.events.edit.listeners) != 'undefined') {
43026             
43027             this.editicon = this.wrap.createChild(
43028                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43029             if (this.addicon) {
43030                 this.editicon.setStyle('margin-left', '40px');
43031             }
43032             this.editicon.on('click', function(e) {
43033                 
43034                 // we fire even  if inothing is selected..
43035                 this.fireEvent('edit', this, this.lastData );
43036                 
43037             }, this);
43038         }
43039         
43040         
43041         
43042     },
43043
43044     // private
43045     initEvents : function(){
43046         Roo.form.ComboBox.superclass.initEvents.call(this);
43047
43048         this.keyNav = new Roo.KeyNav(this.el, {
43049             "up" : function(e){
43050                 this.inKeyMode = true;
43051                 this.selectPrev();
43052             },
43053
43054             "down" : function(e){
43055                 if(!this.isExpanded()){
43056                     this.onTriggerClick();
43057                 }else{
43058                     this.inKeyMode = true;
43059                     this.selectNext();
43060                 }
43061             },
43062
43063             "enter" : function(e){
43064                 this.onViewClick();
43065                 //return true;
43066             },
43067
43068             "esc" : function(e){
43069                 this.collapse();
43070             },
43071
43072             "tab" : function(e){
43073                 this.onViewClick(false);
43074                 this.fireEvent("specialkey", this, e);
43075                 return true;
43076             },
43077
43078             scope : this,
43079
43080             doRelay : function(foo, bar, hname){
43081                 if(hname == 'down' || this.scope.isExpanded()){
43082                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43083                 }
43084                 return true;
43085             },
43086
43087             forceKeyDown: true
43088         });
43089         this.queryDelay = Math.max(this.queryDelay || 10,
43090                 this.mode == 'local' ? 10 : 250);
43091         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43092         if(this.typeAhead){
43093             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43094         }
43095         if(this.editable !== false){
43096             this.el.on("keyup", this.onKeyUp, this);
43097         }
43098         if(this.forceSelection){
43099             this.on('blur', this.doForce, this);
43100         }
43101     },
43102
43103     onDestroy : function(){
43104         if(this.view){
43105             this.view.setStore(null);
43106             this.view.el.removeAllListeners();
43107             this.view.el.remove();
43108             this.view.purgeListeners();
43109         }
43110         if(this.list){
43111             this.list.destroy();
43112         }
43113         if(this.store){
43114             this.store.un('beforeload', this.onBeforeLoad, this);
43115             this.store.un('load', this.onLoad, this);
43116             this.store.un('loadexception', this.onLoadException, this);
43117         }
43118         Roo.form.ComboBox.superclass.onDestroy.call(this);
43119     },
43120
43121     // private
43122     fireKey : function(e){
43123         if(e.isNavKeyPress() && !this.list.isVisible()){
43124             this.fireEvent("specialkey", this, e);
43125         }
43126     },
43127
43128     // private
43129     onResize: function(w, h){
43130         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43131         
43132         if(typeof w != 'number'){
43133             // we do not handle it!?!?
43134             return;
43135         }
43136         var tw = this.trigger.getWidth();
43137         tw += this.addicon ? this.addicon.getWidth() : 0;
43138         tw += this.editicon ? this.editicon.getWidth() : 0;
43139         var x = w - tw;
43140         this.el.setWidth( this.adjustWidth('input', x));
43141             
43142         this.trigger.setStyle('left', x+'px');
43143         
43144         if(this.list && this.listWidth === undefined){
43145             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43146             this.list.setWidth(lw);
43147             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43148         }
43149         
43150     
43151         
43152     },
43153
43154     /**
43155      * Allow or prevent the user from directly editing the field text.  If false is passed,
43156      * the user will only be able to select from the items defined in the dropdown list.  This method
43157      * is the runtime equivalent of setting the 'editable' config option at config time.
43158      * @param {Boolean} value True to allow the user to directly edit the field text
43159      */
43160     setEditable : function(value){
43161         if(value == this.editable){
43162             return;
43163         }
43164         this.editable = value;
43165         if(!value){
43166             this.el.dom.setAttribute('readOnly', true);
43167             this.el.on('mousedown', this.onTriggerClick,  this);
43168             this.el.addClass('x-combo-noedit');
43169         }else{
43170             this.el.dom.setAttribute('readOnly', false);
43171             this.el.un('mousedown', this.onTriggerClick,  this);
43172             this.el.removeClass('x-combo-noedit');
43173         }
43174     },
43175
43176     // private
43177     onBeforeLoad : function(){
43178         if(!this.hasFocus){
43179             return;
43180         }
43181         this.innerList.update(this.loadingText ?
43182                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43183         this.restrictHeight();
43184         this.selectedIndex = -1;
43185     },
43186
43187     // private
43188     onLoad : function(){
43189         if(!this.hasFocus){
43190             return;
43191         }
43192         if(this.store.getCount() > 0){
43193             this.expand();
43194             this.restrictHeight();
43195             if(this.lastQuery == this.allQuery){
43196                 if(this.editable){
43197                     this.el.dom.select();
43198                 }
43199                 if(!this.selectByValue(this.value, true)){
43200                     this.select(0, true);
43201                 }
43202             }else{
43203                 this.selectNext();
43204                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43205                     this.taTask.delay(this.typeAheadDelay);
43206                 }
43207             }
43208         }else{
43209             this.onEmptyResults();
43210         }
43211         //this.el.focus();
43212     },
43213     // private
43214     onLoadException : function()
43215     {
43216         this.collapse();
43217         Roo.log(this.store.reader.jsonData);
43218         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43219             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43220         }
43221         
43222         
43223     },
43224     // private
43225     onTypeAhead : function(){
43226         if(this.store.getCount() > 0){
43227             var r = this.store.getAt(0);
43228             var newValue = r.data[this.displayField];
43229             var len = newValue.length;
43230             var selStart = this.getRawValue().length;
43231             if(selStart != len){
43232                 this.setRawValue(newValue);
43233                 this.selectText(selStart, newValue.length);
43234             }
43235         }
43236     },
43237
43238     // private
43239     onSelect : function(record, index){
43240         if(this.fireEvent('beforeselect', this, record, index) !== false){
43241             this.setFromData(index > -1 ? record.data : false);
43242             this.collapse();
43243             this.fireEvent('select', this, record, index);
43244         }
43245     },
43246
43247     /**
43248      * Returns the currently selected field value or empty string if no value is set.
43249      * @return {String} value The selected value
43250      */
43251     getValue : function(){
43252         if(this.valueField){
43253             return typeof this.value != 'undefined' ? this.value : '';
43254         }
43255         return Roo.form.ComboBox.superclass.getValue.call(this);
43256     },
43257
43258     /**
43259      * Clears any text/value currently set in the field
43260      */
43261     clearValue : function(){
43262         if(this.hiddenField){
43263             this.hiddenField.value = '';
43264         }
43265         this.value = '';
43266         this.setRawValue('');
43267         this.lastSelectionText = '';
43268         
43269     },
43270
43271     /**
43272      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43273      * will be displayed in the field.  If the value does not match the data value of an existing item,
43274      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43275      * Otherwise the field will be blank (although the value will still be set).
43276      * @param {String} value The value to match
43277      */
43278     setValue : function(v){
43279         var text = v;
43280         if(this.valueField){
43281             var r = this.findRecord(this.valueField, v);
43282             if(r){
43283                 text = r.data[this.displayField];
43284             }else if(this.valueNotFoundText !== undefined){
43285                 text = this.valueNotFoundText;
43286             }
43287         }
43288         this.lastSelectionText = text;
43289         if(this.hiddenField){
43290             this.hiddenField.value = v;
43291         }
43292         Roo.form.ComboBox.superclass.setValue.call(this, text);
43293         this.value = v;
43294     },
43295     /**
43296      * @property {Object} the last set data for the element
43297      */
43298     
43299     lastData : false,
43300     /**
43301      * Sets the value of the field based on a object which is related to the record format for the store.
43302      * @param {Object} value the value to set as. or false on reset?
43303      */
43304     setFromData : function(o){
43305         var dv = ''; // display value
43306         var vv = ''; // value value..
43307         this.lastData = o;
43308         if (this.displayField) {
43309             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43310         } else {
43311             // this is an error condition!!!
43312             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43313         }
43314         
43315         if(this.valueField){
43316             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43317         }
43318         if(this.hiddenField){
43319             this.hiddenField.value = vv;
43320             
43321             this.lastSelectionText = dv;
43322             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43323             this.value = vv;
43324             return;
43325         }
43326         // no hidden field.. - we store the value in 'value', but still display
43327         // display field!!!!
43328         this.lastSelectionText = dv;
43329         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43330         this.value = vv;
43331         
43332         
43333     },
43334     // private
43335     reset : function(){
43336         // overridden so that last data is reset..
43337         this.setValue(this.resetValue);
43338         this.originalValue = this.getValue();
43339         this.clearInvalid();
43340         this.lastData = false;
43341         if (this.view) {
43342             this.view.clearSelections();
43343         }
43344     },
43345     // private
43346     findRecord : function(prop, value){
43347         var record;
43348         if(this.store.getCount() > 0){
43349             this.store.each(function(r){
43350                 if(r.data[prop] == value){
43351                     record = r;
43352                     return false;
43353                 }
43354                 return true;
43355             });
43356         }
43357         return record;
43358     },
43359     
43360     getName: function()
43361     {
43362         // returns hidden if it's set..
43363         if (!this.rendered) {return ''};
43364         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43365         
43366     },
43367     // private
43368     onViewMove : function(e, t){
43369         this.inKeyMode = false;
43370     },
43371
43372     // private
43373     onViewOver : function(e, t){
43374         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43375             return;
43376         }
43377         var item = this.view.findItemFromChild(t);
43378         if(item){
43379             var index = this.view.indexOf(item);
43380             this.select(index, false);
43381         }
43382     },
43383
43384     // private
43385     onViewClick : function(doFocus)
43386     {
43387         var index = this.view.getSelectedIndexes()[0];
43388         var r = this.store.getAt(index);
43389         if(r){
43390             this.onSelect(r, index);
43391         }
43392         if(doFocus !== false && !this.blockFocus){
43393             this.el.focus();
43394         }
43395     },
43396
43397     // private
43398     restrictHeight : function(){
43399         this.innerList.dom.style.height = '';
43400         var inner = this.innerList.dom;
43401         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43402         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43403         this.list.beginUpdate();
43404         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43405         this.list.alignTo(this.el, this.listAlign);
43406         this.list.endUpdate();
43407     },
43408
43409     // private
43410     onEmptyResults : function(){
43411         this.collapse();
43412     },
43413
43414     /**
43415      * Returns true if the dropdown list is expanded, else false.
43416      */
43417     isExpanded : function(){
43418         return this.list.isVisible();
43419     },
43420
43421     /**
43422      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43423      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43424      * @param {String} value The data value of the item to select
43425      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43426      * selected item if it is not currently in view (defaults to true)
43427      * @return {Boolean} True if the value matched an item in the list, else false
43428      */
43429     selectByValue : function(v, scrollIntoView){
43430         if(v !== undefined && v !== null){
43431             var r = this.findRecord(this.valueField || this.displayField, v);
43432             if(r){
43433                 this.select(this.store.indexOf(r), scrollIntoView);
43434                 return true;
43435             }
43436         }
43437         return false;
43438     },
43439
43440     /**
43441      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43442      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43443      * @param {Number} index The zero-based index of the list item to select
43444      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43445      * selected item if it is not currently in view (defaults to true)
43446      */
43447     select : function(index, scrollIntoView){
43448         this.selectedIndex = index;
43449         this.view.select(index);
43450         if(scrollIntoView !== false){
43451             var el = this.view.getNode(index);
43452             if(el){
43453                 this.innerList.scrollChildIntoView(el, false);
43454             }
43455         }
43456     },
43457
43458     // private
43459     selectNext : function(){
43460         var ct = this.store.getCount();
43461         if(ct > 0){
43462             if(this.selectedIndex == -1){
43463                 this.select(0);
43464             }else if(this.selectedIndex < ct-1){
43465                 this.select(this.selectedIndex+1);
43466             }
43467         }
43468     },
43469
43470     // private
43471     selectPrev : function(){
43472         var ct = this.store.getCount();
43473         if(ct > 0){
43474             if(this.selectedIndex == -1){
43475                 this.select(0);
43476             }else if(this.selectedIndex != 0){
43477                 this.select(this.selectedIndex-1);
43478             }
43479         }
43480     },
43481
43482     // private
43483     onKeyUp : function(e){
43484         if(this.editable !== false && !e.isSpecialKey()){
43485             this.lastKey = e.getKey();
43486             this.dqTask.delay(this.queryDelay);
43487         }
43488     },
43489
43490     // private
43491     validateBlur : function(){
43492         return !this.list || !this.list.isVisible();   
43493     },
43494
43495     // private
43496     initQuery : function(){
43497         this.doQuery(this.getRawValue());
43498     },
43499
43500     // private
43501     doForce : function(){
43502         if(this.el.dom.value.length > 0){
43503             this.el.dom.value =
43504                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43505              
43506         }
43507     },
43508
43509     /**
43510      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43511      * query allowing the query action to be canceled if needed.
43512      * @param {String} query The SQL query to execute
43513      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43514      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43515      * saved in the current store (defaults to false)
43516      */
43517     doQuery : function(q, forceAll){
43518         if(q === undefined || q === null){
43519             q = '';
43520         }
43521         var qe = {
43522             query: q,
43523             forceAll: forceAll,
43524             combo: this,
43525             cancel:false
43526         };
43527         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43528             return false;
43529         }
43530         q = qe.query;
43531         forceAll = qe.forceAll;
43532         if(forceAll === true || (q.length >= this.minChars)){
43533             if(this.lastQuery != q || this.alwaysQuery){
43534                 this.lastQuery = q;
43535                 if(this.mode == 'local'){
43536                     this.selectedIndex = -1;
43537                     if(forceAll){
43538                         this.store.clearFilter();
43539                     }else{
43540                         this.store.filter(this.displayField, q);
43541                     }
43542                     this.onLoad();
43543                 }else{
43544                     this.store.baseParams[this.queryParam] = q;
43545                     this.store.load({
43546                         params: this.getParams(q)
43547                     });
43548                     this.expand();
43549                 }
43550             }else{
43551                 this.selectedIndex = -1;
43552                 this.onLoad();   
43553             }
43554         }
43555     },
43556
43557     // private
43558     getParams : function(q){
43559         var p = {};
43560         //p[this.queryParam] = q;
43561         if(this.pageSize){
43562             p.start = 0;
43563             p.limit = this.pageSize;
43564         }
43565         return p;
43566     },
43567
43568     /**
43569      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43570      */
43571     collapse : function(){
43572         if(!this.isExpanded()){
43573             return;
43574         }
43575         this.list.hide();
43576         Roo.get(document).un('mousedown', this.collapseIf, this);
43577         Roo.get(document).un('mousewheel', this.collapseIf, this);
43578         if (!this.editable) {
43579             Roo.get(document).un('keydown', this.listKeyPress, this);
43580         }
43581         this.fireEvent('collapse', this);
43582     },
43583
43584     // private
43585     collapseIf : function(e){
43586         if(!e.within(this.wrap) && !e.within(this.list)){
43587             this.collapse();
43588         }
43589     },
43590
43591     /**
43592      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43593      */
43594     expand : function(){
43595         if(this.isExpanded() || !this.hasFocus){
43596             return;
43597         }
43598         this.list.alignTo(this.el, this.listAlign);
43599         this.list.show();
43600         Roo.get(document).on('mousedown', this.collapseIf, this);
43601         Roo.get(document).on('mousewheel', this.collapseIf, this);
43602         if (!this.editable) {
43603             Roo.get(document).on('keydown', this.listKeyPress, this);
43604         }
43605         
43606         this.fireEvent('expand', this);
43607     },
43608
43609     // private
43610     // Implements the default empty TriggerField.onTriggerClick function
43611     onTriggerClick : function(){
43612         if(this.disabled){
43613             return;
43614         }
43615         if(this.isExpanded()){
43616             this.collapse();
43617             if (!this.blockFocus) {
43618                 this.el.focus();
43619             }
43620             
43621         }else {
43622             this.hasFocus = true;
43623             if(this.triggerAction == 'all') {
43624                 this.doQuery(this.allQuery, true);
43625             } else {
43626                 this.doQuery(this.getRawValue());
43627             }
43628             if (!this.blockFocus) {
43629                 this.el.focus();
43630             }
43631         }
43632     },
43633     listKeyPress : function(e)
43634     {
43635         //Roo.log('listkeypress');
43636         // scroll to first matching element based on key pres..
43637         if (e.isSpecialKey()) {
43638             return false;
43639         }
43640         var k = String.fromCharCode(e.getKey()).toUpperCase();
43641         //Roo.log(k);
43642         var match  = false;
43643         var csel = this.view.getSelectedNodes();
43644         var cselitem = false;
43645         if (csel.length) {
43646             var ix = this.view.indexOf(csel[0]);
43647             cselitem  = this.store.getAt(ix);
43648             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43649                 cselitem = false;
43650             }
43651             
43652         }
43653         
43654         this.store.each(function(v) { 
43655             if (cselitem) {
43656                 // start at existing selection.
43657                 if (cselitem.id == v.id) {
43658                     cselitem = false;
43659                 }
43660                 return;
43661             }
43662                 
43663             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43664                 match = this.store.indexOf(v);
43665                 return false;
43666             }
43667         }, this);
43668         
43669         if (match === false) {
43670             return true; // no more action?
43671         }
43672         // scroll to?
43673         this.view.select(match);
43674         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43675         sn.scrollIntoView(sn.dom.parentNode, false);
43676     } 
43677
43678     /** 
43679     * @cfg {Boolean} grow 
43680     * @hide 
43681     */
43682     /** 
43683     * @cfg {Number} growMin 
43684     * @hide 
43685     */
43686     /** 
43687     * @cfg {Number} growMax 
43688     * @hide 
43689     */
43690     /**
43691      * @hide
43692      * @method autoSize
43693      */
43694 });/*
43695  * Copyright(c) 2010-2012, Roo J Solutions Limited
43696  *
43697  * Licence LGPL
43698  *
43699  */
43700
43701 /**
43702  * @class Roo.form.ComboBoxArray
43703  * @extends Roo.form.TextField
43704  * A facebook style adder... for lists of email / people / countries  etc...
43705  * pick multiple items from a combo box, and shows each one.
43706  *
43707  *  Fred [x]  Brian [x]  [Pick another |v]
43708  *
43709  *
43710  *  For this to work: it needs various extra information
43711  *    - normal combo problay has
43712  *      name, hiddenName
43713  *    + displayField, valueField
43714  *
43715  *    For our purpose...
43716  *
43717  *
43718  *   If we change from 'extends' to wrapping...
43719  *   
43720  *  
43721  *
43722  
43723  
43724  * @constructor
43725  * Create a new ComboBoxArray.
43726  * @param {Object} config Configuration options
43727  */
43728  
43729
43730 Roo.form.ComboBoxArray = function(config)
43731 {
43732     this.addEvents({
43733         /**
43734          * @event beforeremove
43735          * Fires before remove the value from the list
43736              * @param {Roo.form.ComboBoxArray} _self This combo box array
43737              * @param {Roo.form.ComboBoxArray.Item} item removed item
43738              */
43739         'beforeremove' : true,
43740         /**
43741          * @event remove
43742          * Fires when remove the value from the list
43743              * @param {Roo.form.ComboBoxArray} _self This combo box array
43744              * @param {Roo.form.ComboBoxArray.Item} item removed item
43745              */
43746         'remove' : true
43747         
43748         
43749     });
43750     
43751     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43752     
43753     this.items = new Roo.util.MixedCollection(false);
43754     
43755     // construct the child combo...
43756     
43757     
43758     
43759     
43760    
43761     
43762 }
43763
43764  
43765 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43766
43767     /**
43768      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43769      */
43770     
43771     lastData : false,
43772     
43773     // behavies liek a hiddne field
43774     inputType:      'hidden',
43775     /**
43776      * @cfg {Number} width The width of the box that displays the selected element
43777      */ 
43778     width:          300,
43779
43780     
43781     
43782     /**
43783      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43784      */
43785     name : false,
43786     /**
43787      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43788      */
43789     hiddenName : false,
43790       /**
43791      * @cfg {String} seperator    The value seperator normally ',' 
43792      */
43793     seperator : ',',
43794     
43795     // private the array of items that are displayed..
43796     items  : false,
43797     // private - the hidden field el.
43798     hiddenEl : false,
43799     // private - the filed el..
43800     el : false,
43801     
43802     //validateValue : function() { return true; }, // all values are ok!
43803     //onAddClick: function() { },
43804     
43805     onRender : function(ct, position) 
43806     {
43807         
43808         // create the standard hidden element
43809         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43810         
43811         
43812         // give fake names to child combo;
43813         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43814         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43815         
43816         this.combo = Roo.factory(this.combo, Roo.form);
43817         this.combo.onRender(ct, position);
43818         if (typeof(this.combo.width) != 'undefined') {
43819             this.combo.onResize(this.combo.width,0);
43820         }
43821         
43822         this.combo.initEvents();
43823         
43824         // assigned so form know we need to do this..
43825         this.store          = this.combo.store;
43826         this.valueField     = this.combo.valueField;
43827         this.displayField   = this.combo.displayField ;
43828         
43829         
43830         this.combo.wrap.addClass('x-cbarray-grp');
43831         
43832         var cbwrap = this.combo.wrap.createChild(
43833             {tag: 'div', cls: 'x-cbarray-cb'},
43834             this.combo.el.dom
43835         );
43836         
43837              
43838         this.hiddenEl = this.combo.wrap.createChild({
43839             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43840         });
43841         this.el = this.combo.wrap.createChild({
43842             tag: 'input',  type:'hidden' , name: this.name, value : ''
43843         });
43844          //   this.el.dom.removeAttribute("name");
43845         
43846         
43847         this.outerWrap = this.combo.wrap;
43848         this.wrap = cbwrap;
43849         
43850         this.outerWrap.setWidth(this.width);
43851         this.outerWrap.dom.removeChild(this.el.dom);
43852         
43853         this.wrap.dom.appendChild(this.el.dom);
43854         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43855         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43856         
43857         this.combo.trigger.setStyle('position','relative');
43858         this.combo.trigger.setStyle('left', '0px');
43859         this.combo.trigger.setStyle('top', '2px');
43860         
43861         this.combo.el.setStyle('vertical-align', 'text-bottom');
43862         
43863         //this.trigger.setStyle('vertical-align', 'top');
43864         
43865         // this should use the code from combo really... on('add' ....)
43866         if (this.adder) {
43867             
43868         
43869             this.adder = this.outerWrap.createChild(
43870                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43871             var _t = this;
43872             this.adder.on('click', function(e) {
43873                 _t.fireEvent('adderclick', this, e);
43874             }, _t);
43875         }
43876         //var _t = this;
43877         //this.adder.on('click', this.onAddClick, _t);
43878         
43879         
43880         this.combo.on('select', function(cb, rec, ix) {
43881             this.addItem(rec.data);
43882             
43883             cb.setValue('');
43884             cb.el.dom.value = '';
43885             //cb.lastData = rec.data;
43886             // add to list
43887             
43888         }, this);
43889         
43890         
43891     },
43892     
43893     
43894     getName: function()
43895     {
43896         // returns hidden if it's set..
43897         if (!this.rendered) {return ''};
43898         return  this.hiddenName ? this.hiddenName : this.name;
43899         
43900     },
43901     
43902     
43903     onResize: function(w, h){
43904         
43905         return;
43906         // not sure if this is needed..
43907         //this.combo.onResize(w,h);
43908         
43909         if(typeof w != 'number'){
43910             // we do not handle it!?!?
43911             return;
43912         }
43913         var tw = this.combo.trigger.getWidth();
43914         tw += this.addicon ? this.addicon.getWidth() : 0;
43915         tw += this.editicon ? this.editicon.getWidth() : 0;
43916         var x = w - tw;
43917         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43918             
43919         this.combo.trigger.setStyle('left', '0px');
43920         
43921         if(this.list && this.listWidth === undefined){
43922             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43923             this.list.setWidth(lw);
43924             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43925         }
43926         
43927     
43928         
43929     },
43930     
43931     addItem: function(rec)
43932     {
43933         var valueField = this.combo.valueField;
43934         var displayField = this.combo.displayField;
43935         
43936         if (this.items.indexOfKey(rec[valueField]) > -1) {
43937             //console.log("GOT " + rec.data.id);
43938             return;
43939         }
43940         
43941         var x = new Roo.form.ComboBoxArray.Item({
43942             //id : rec[this.idField],
43943             data : rec,
43944             displayField : displayField ,
43945             tipField : displayField ,
43946             cb : this
43947         });
43948         // use the 
43949         this.items.add(rec[valueField],x);
43950         // add it before the element..
43951         this.updateHiddenEl();
43952         x.render(this.outerWrap, this.wrap.dom);
43953         // add the image handler..
43954     },
43955     
43956     updateHiddenEl : function()
43957     {
43958         this.validate();
43959         if (!this.hiddenEl) {
43960             return;
43961         }
43962         var ar = [];
43963         var idField = this.combo.valueField;
43964         
43965         this.items.each(function(f) {
43966             ar.push(f.data[idField]);
43967         });
43968         this.hiddenEl.dom.value = ar.join(this.seperator);
43969         this.validate();
43970     },
43971     
43972     reset : function()
43973     {
43974         this.items.clear();
43975         
43976         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43977            el.remove();
43978         });
43979         
43980         this.el.dom.value = '';
43981         if (this.hiddenEl) {
43982             this.hiddenEl.dom.value = '';
43983         }
43984         
43985     },
43986     getValue: function()
43987     {
43988         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43989     },
43990     setValue: function(v) // not a valid action - must use addItems..
43991     {
43992         
43993         this.reset();
43994          
43995         if (this.store.isLocal && (typeof(v) == 'string')) {
43996             // then we can use the store to find the values..
43997             // comma seperated at present.. this needs to allow JSON based encoding..
43998             this.hiddenEl.value  = v;
43999             var v_ar = [];
44000             Roo.each(v.split(this.seperator), function(k) {
44001                 Roo.log("CHECK " + this.valueField + ',' + k);
44002                 var li = this.store.query(this.valueField, k);
44003                 if (!li.length) {
44004                     return;
44005                 }
44006                 var add = {};
44007                 add[this.valueField] = k;
44008                 add[this.displayField] = li.item(0).data[this.displayField];
44009                 
44010                 this.addItem(add);
44011             }, this) 
44012              
44013         }
44014         if (typeof(v) == 'object' ) {
44015             // then let's assume it's an array of objects..
44016             Roo.each(v, function(l) {
44017                 var add = l;
44018                 if (typeof(l) == 'string') {
44019                     add = {};
44020                     add[this.valueField] = l;
44021                     add[this.displayField] = l
44022                 }
44023                 this.addItem(add);
44024             }, this);
44025              
44026         }
44027         
44028         
44029     },
44030     setFromData: function(v)
44031     {
44032         // this recieves an object, if setValues is called.
44033         this.reset();
44034         this.el.dom.value = v[this.displayField];
44035         this.hiddenEl.dom.value = v[this.valueField];
44036         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44037             return;
44038         }
44039         var kv = v[this.valueField];
44040         var dv = v[this.displayField];
44041         kv = typeof(kv) != 'string' ? '' : kv;
44042         dv = typeof(dv) != 'string' ? '' : dv;
44043         
44044         
44045         var keys = kv.split(this.seperator);
44046         var display = dv.split(this.seperator);
44047         for (var i = 0 ; i < keys.length; i++) {
44048             add = {};
44049             add[this.valueField] = keys[i];
44050             add[this.displayField] = display[i];
44051             this.addItem(add);
44052         }
44053       
44054         
44055     },
44056     
44057     /**
44058      * Validates the combox array value
44059      * @return {Boolean} True if the value is valid, else false
44060      */
44061     validate : function(){
44062         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44063             this.clearInvalid();
44064             return true;
44065         }
44066         return false;
44067     },
44068     
44069     validateValue : function(value){
44070         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44071         
44072     },
44073     
44074     /*@
44075      * overide
44076      * 
44077      */
44078     isDirty : function() {
44079         if(this.disabled) {
44080             return false;
44081         }
44082         
44083         try {
44084             var d = Roo.decode(String(this.originalValue));
44085         } catch (e) {
44086             return String(this.getValue()) !== String(this.originalValue);
44087         }
44088         
44089         var originalValue = [];
44090         
44091         for (var i = 0; i < d.length; i++){
44092             originalValue.push(d[i][this.valueField]);
44093         }
44094         
44095         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44096         
44097     }
44098     
44099 });
44100
44101
44102
44103 /**
44104  * @class Roo.form.ComboBoxArray.Item
44105  * @extends Roo.BoxComponent
44106  * A selected item in the list
44107  *  Fred [x]  Brian [x]  [Pick another |v]
44108  * 
44109  * @constructor
44110  * Create a new item.
44111  * @param {Object} config Configuration options
44112  */
44113  
44114 Roo.form.ComboBoxArray.Item = function(config) {
44115     config.id = Roo.id();
44116     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44117 }
44118
44119 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44120     data : {},
44121     cb: false,
44122     displayField : false,
44123     tipField : false,
44124     
44125     
44126     defaultAutoCreate : {
44127         tag: 'div',
44128         cls: 'x-cbarray-item',
44129         cn : [ 
44130             { tag: 'div' },
44131             {
44132                 tag: 'img',
44133                 width:16,
44134                 height : 16,
44135                 src : Roo.BLANK_IMAGE_URL ,
44136                 align: 'center'
44137             }
44138         ]
44139         
44140     },
44141     
44142  
44143     onRender : function(ct, position)
44144     {
44145         Roo.form.Field.superclass.onRender.call(this, ct, position);
44146         
44147         if(!this.el){
44148             var cfg = this.getAutoCreate();
44149             this.el = ct.createChild(cfg, position);
44150         }
44151         
44152         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44153         
44154         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44155             this.cb.renderer(this.data) :
44156             String.format('{0}',this.data[this.displayField]);
44157         
44158             
44159         this.el.child('div').dom.setAttribute('qtip',
44160                         String.format('{0}',this.data[this.tipField])
44161         );
44162         
44163         this.el.child('img').on('click', this.remove, this);
44164         
44165     },
44166    
44167     remove : function()
44168     {
44169         if(this.cb.disabled){
44170             return;
44171         }
44172         
44173         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44174             this.cb.items.remove(this);
44175             this.el.child('img').un('click', this.remove, this);
44176             this.el.remove();
44177             this.cb.updateHiddenEl();
44178
44179             this.cb.fireEvent('remove', this.cb, this);
44180         }
44181         
44182     }
44183 });/*
44184  * RooJS Library 1.1.1
44185  * Copyright(c) 2008-2011  Alan Knowles
44186  *
44187  * License - LGPL
44188  */
44189  
44190
44191 /**
44192  * @class Roo.form.ComboNested
44193  * @extends Roo.form.ComboBox
44194  * A combobox for that allows selection of nested items in a list,
44195  * eg.
44196  *
44197  *  Book
44198  *    -> red
44199  *    -> green
44200  *  Table
44201  *    -> square
44202  *      ->red
44203  *      ->green
44204  *    -> rectangle
44205  *      ->green
44206  *      
44207  * 
44208  * @constructor
44209  * Create a new ComboNested
44210  * @param {Object} config Configuration options
44211  */
44212 Roo.form.ComboNested = function(config){
44213     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44214     // should verify some data...
44215     // like
44216     // hiddenName = required..
44217     // displayField = required
44218     // valudField == required
44219     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44220     var _t = this;
44221     Roo.each(req, function(e) {
44222         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44223             throw "Roo.form.ComboNested : missing value for: " + e;
44224         }
44225     });
44226      
44227     
44228 };
44229
44230 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44231    
44232     /*
44233      * @config {Number} max Number of columns to show
44234      */
44235     
44236     maxColumns : 3,
44237    
44238     list : null, // the outermost div..
44239     innerLists : null, // the
44240     views : null,
44241     stores : null,
44242     // private
44243     loadingChildren : false,
44244     
44245     onRender : function(ct, position)
44246     {
44247         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44248         
44249         if(this.hiddenName){
44250             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44251                     'before', true);
44252             this.hiddenField.value =
44253                 this.hiddenValue !== undefined ? this.hiddenValue :
44254                 this.value !== undefined ? this.value : '';
44255
44256             // prevent input submission
44257             this.el.dom.removeAttribute('name');
44258              
44259              
44260         }
44261         
44262         if(Roo.isGecko){
44263             this.el.dom.setAttribute('autocomplete', 'off');
44264         }
44265
44266         var cls = 'x-combo-list';
44267
44268         this.list = new Roo.Layer({
44269             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44270         });
44271
44272         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44273         this.list.setWidth(lw);
44274         this.list.swallowEvent('mousewheel');
44275         this.assetHeight = 0;
44276
44277         if(this.title){
44278             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44279             this.assetHeight += this.header.getHeight();
44280         }
44281         this.innerLists = [];
44282         this.views = [];
44283         this.stores = [];
44284         for (var i =0 ; i < this.maxColumns; i++) {
44285             this.onRenderList( cls, i);
44286         }
44287         
44288         // always needs footer, as we are going to have an 'OK' button.
44289         this.footer = this.list.createChild({cls:cls+'-ft'});
44290         this.pageTb = new Roo.Toolbar(this.footer);  
44291         var _this = this;
44292         this.pageTb.add(  {
44293             
44294             text: 'Done',
44295             handler: function()
44296             {
44297                 _this.collapse();
44298             }
44299         });
44300         
44301         if ( this.allowBlank && !this.disableClear) {
44302             
44303             this.pageTb.add(new Roo.Toolbar.Fill(), {
44304                 cls: 'x-btn-icon x-btn-clear',
44305                 text: '&#160;',
44306                 handler: function()
44307                 {
44308                     _this.collapse();
44309                     _this.clearValue();
44310                     _this.onSelect(false, -1);
44311                 }
44312             });
44313         }
44314         if (this.footer) {
44315             this.assetHeight += this.footer.getHeight();
44316         }
44317         
44318     },
44319     onRenderList : function (  cls, i)
44320     {
44321         
44322         var lw = Math.floor(
44323                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44324         );
44325         
44326         this.list.setWidth(lw); // default to '1'
44327
44328         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44329         //il.on('mouseover', this.onViewOver, this, { list:  i });
44330         //il.on('mousemove', this.onViewMove, this, { list:  i });
44331         il.setWidth(lw);
44332         il.setStyle({ 'overflow-x' : 'hidden'});
44333
44334         if(!this.tpl){
44335             this.tpl = new Roo.Template({
44336                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44337                 isEmpty: function (value, allValues) {
44338                     //Roo.log(value);
44339                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44340                     return dl ? 'has-children' : 'no-children'
44341                 }
44342             });
44343         }
44344         
44345         var store  = this.store;
44346         if (i > 0) {
44347             store  = new Roo.data.SimpleStore({
44348                 //fields : this.store.reader.meta.fields,
44349                 reader : this.store.reader,
44350                 data : [ ]
44351             });
44352         }
44353         this.stores[i]  = store;
44354                   
44355         var view = this.views[i] = new Roo.View(
44356             il,
44357             this.tpl,
44358             {
44359                 singleSelect:true,
44360                 store: store,
44361                 selectedClass: this.selectedClass
44362             }
44363         );
44364         view.getEl().setWidth(lw);
44365         view.getEl().setStyle({
44366             position: i < 1 ? 'relative' : 'absolute',
44367             top: 0,
44368             left: (i * lw ) + 'px',
44369             display : i > 0 ? 'none' : 'block'
44370         });
44371         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44372         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44373         //view.on('click', this.onViewClick, this, { list : i });
44374
44375         store.on('beforeload', this.onBeforeLoad, this);
44376         store.on('load',  this.onLoad, this, { list  : i});
44377         store.on('loadexception', this.onLoadException, this);
44378
44379         // hide the other vies..
44380         
44381         
44382         
44383     },
44384       
44385     restrictHeight : function()
44386     {
44387         var mh = 0;
44388         Roo.each(this.innerLists, function(il,i) {
44389             var el = this.views[i].getEl();
44390             el.dom.style.height = '';
44391             var inner = el.dom;
44392             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44393             // only adjust heights on other ones..
44394             mh = Math.max(h, mh);
44395             if (i < 1) {
44396                 
44397                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44398                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44399                
44400             }
44401             
44402             
44403         }, this);
44404         
44405         this.list.beginUpdate();
44406         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44407         this.list.alignTo(this.el, this.listAlign);
44408         this.list.endUpdate();
44409         
44410     },
44411      
44412     
44413     // -- store handlers..
44414     // private
44415     onBeforeLoad : function()
44416     {
44417         if(!this.hasFocus){
44418             return;
44419         }
44420         this.innerLists[0].update(this.loadingText ?
44421                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44422         this.restrictHeight();
44423         this.selectedIndex = -1;
44424     },
44425     // private
44426     onLoad : function(a,b,c,d)
44427     {
44428         if (!this.loadingChildren) {
44429             // then we are loading the top level. - hide the children
44430             for (var i = 1;i < this.views.length; i++) {
44431                 this.views[i].getEl().setStyle({ display : 'none' });
44432             }
44433             var lw = Math.floor(
44434                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44435             );
44436         
44437              this.list.setWidth(lw); // default to '1'
44438
44439             
44440         }
44441         if(!this.hasFocus){
44442             return;
44443         }
44444         
44445         if(this.store.getCount() > 0) {
44446             this.expand();
44447             this.restrictHeight();   
44448         } else {
44449             this.onEmptyResults();
44450         }
44451         
44452         if (!this.loadingChildren) {
44453             this.selectActive();
44454         }
44455         /*
44456         this.stores[1].loadData([]);
44457         this.stores[2].loadData([]);
44458         this.views
44459         */    
44460     
44461         //this.el.focus();
44462     },
44463     
44464     
44465     // private
44466     onLoadException : function()
44467     {
44468         this.collapse();
44469         Roo.log(this.store.reader.jsonData);
44470         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44471             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44472         }
44473         
44474         
44475     },
44476     // no cleaning of leading spaces on blur here.
44477     cleanLeadingSpace : function(e) { },
44478     
44479
44480     onSelectChange : function (view, sels, opts )
44481     {
44482         var ix = view.getSelectedIndexes();
44483          
44484         if (opts.list > this.maxColumns - 2) {
44485             if (view.store.getCount()<  1) {
44486                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44487
44488             } else  {
44489                 if (ix.length) {
44490                     // used to clear ?? but if we are loading unselected 
44491                     this.setFromData(view.store.getAt(ix[0]).data);
44492                 }
44493                 
44494             }
44495             
44496             return;
44497         }
44498         
44499         if (!ix.length) {
44500             // this get's fired when trigger opens..
44501            // this.setFromData({});
44502             var str = this.stores[opts.list+1];
44503             str.data.clear(); // removeall wihtout the fire events..
44504             return;
44505         }
44506         
44507         var rec = view.store.getAt(ix[0]);
44508          
44509         this.setFromData(rec.data);
44510         this.fireEvent('select', this, rec, ix[0]);
44511         
44512         var lw = Math.floor(
44513              (
44514                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44515              ) / this.maxColumns
44516         );
44517         this.loadingChildren = true;
44518         this.stores[opts.list+1].loadDataFromChildren( rec );
44519         this.loadingChildren = false;
44520         var dl = this.stores[opts.list+1]. getTotalCount();
44521         
44522         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44523         
44524         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44525         for (var i = opts.list+2; i < this.views.length;i++) {
44526             this.views[i].getEl().setStyle({ display : 'none' });
44527         }
44528         
44529         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44530         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44531         
44532         if (this.isLoading) {
44533            // this.selectActive(opts.list);
44534         }
44535          
44536     },
44537     
44538     
44539     
44540     
44541     onDoubleClick : function()
44542     {
44543         this.collapse(); //??
44544     },
44545     
44546      
44547     
44548     
44549     
44550     // private
44551     recordToStack : function(store, prop, value, stack)
44552     {
44553         var cstore = new Roo.data.SimpleStore({
44554             //fields : this.store.reader.meta.fields, // we need array reader.. for
44555             reader : this.store.reader,
44556             data : [ ]
44557         });
44558         var _this = this;
44559         var record  = false;
44560         var srec = false;
44561         if(store.getCount() < 1){
44562             return false;
44563         }
44564         store.each(function(r){
44565             if(r.data[prop] == value){
44566                 record = r;
44567             srec = r;
44568                 return false;
44569             }
44570             if (r.data.cn && r.data.cn.length) {
44571                 cstore.loadDataFromChildren( r);
44572                 var cret = _this.recordToStack(cstore, prop, value, stack);
44573                 if (cret !== false) {
44574                     record = cret;
44575                     srec = r;
44576                     return false;
44577                 }
44578             }
44579              
44580             return true;
44581         });
44582         if (record == false) {
44583             return false
44584         }
44585         stack.unshift(srec);
44586         return record;
44587     },
44588     
44589     /*
44590      * find the stack of stores that match our value.
44591      *
44592      * 
44593      */
44594     
44595     selectActive : function ()
44596     {
44597         // if store is not loaded, then we will need to wait for that to happen first.
44598         var stack = [];
44599         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44600         for (var i = 0; i < stack.length; i++ ) {
44601             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44602         }
44603         
44604     }
44605         
44606          
44607     
44608     
44609     
44610     
44611 });/*
44612  * Based on:
44613  * Ext JS Library 1.1.1
44614  * Copyright(c) 2006-2007, Ext JS, LLC.
44615  *
44616  * Originally Released Under LGPL - original licence link has changed is not relivant.
44617  *
44618  * Fork - LGPL
44619  * <script type="text/javascript">
44620  */
44621 /**
44622  * @class Roo.form.Checkbox
44623  * @extends Roo.form.Field
44624  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44625  * @constructor
44626  * Creates a new Checkbox
44627  * @param {Object} config Configuration options
44628  */
44629 Roo.form.Checkbox = function(config){
44630     Roo.form.Checkbox.superclass.constructor.call(this, config);
44631     this.addEvents({
44632         /**
44633          * @event check
44634          * Fires when the checkbox is checked or unchecked.
44635              * @param {Roo.form.Checkbox} this This checkbox
44636              * @param {Boolean} checked The new checked value
44637              */
44638         check : true
44639     });
44640 };
44641
44642 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44643     /**
44644      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44645      */
44646     focusClass : undefined,
44647     /**
44648      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44649      */
44650     fieldClass: "x-form-field",
44651     /**
44652      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44653      */
44654     checked: false,
44655     /**
44656      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44657      * {tag: "input", type: "checkbox", autocomplete: "off"})
44658      */
44659     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44660     /**
44661      * @cfg {String} boxLabel The text that appears beside the checkbox
44662      */
44663     boxLabel : "",
44664     /**
44665      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44666      */  
44667     inputValue : '1',
44668     /**
44669      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44670      */
44671      valueOff: '0', // value when not checked..
44672
44673     actionMode : 'viewEl', 
44674     //
44675     // private
44676     itemCls : 'x-menu-check-item x-form-item',
44677     groupClass : 'x-menu-group-item',
44678     inputType : 'hidden',
44679     
44680     
44681     inSetChecked: false, // check that we are not calling self...
44682     
44683     inputElement: false, // real input element?
44684     basedOn: false, // ????
44685     
44686     isFormField: true, // not sure where this is needed!!!!
44687
44688     onResize : function(){
44689         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44690         if(!this.boxLabel){
44691             this.el.alignTo(this.wrap, 'c-c');
44692         }
44693     },
44694
44695     initEvents : function(){
44696         Roo.form.Checkbox.superclass.initEvents.call(this);
44697         this.el.on("click", this.onClick,  this);
44698         this.el.on("change", this.onClick,  this);
44699     },
44700
44701
44702     getResizeEl : function(){
44703         return this.wrap;
44704     },
44705
44706     getPositionEl : function(){
44707         return this.wrap;
44708     },
44709
44710     // private
44711     onRender : function(ct, position){
44712         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44713         /*
44714         if(this.inputValue !== undefined){
44715             this.el.dom.value = this.inputValue;
44716         }
44717         */
44718         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44719         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44720         var viewEl = this.wrap.createChild({ 
44721             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44722         this.viewEl = viewEl;   
44723         this.wrap.on('click', this.onClick,  this); 
44724         
44725         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44726         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44727         
44728         
44729         
44730         if(this.boxLabel){
44731             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44732         //    viewEl.on('click', this.onClick,  this); 
44733         }
44734         //if(this.checked){
44735             this.setChecked(this.checked);
44736         //}else{
44737             //this.checked = this.el.dom;
44738         //}
44739
44740     },
44741
44742     // private
44743     initValue : Roo.emptyFn,
44744
44745     /**
44746      * Returns the checked state of the checkbox.
44747      * @return {Boolean} True if checked, else false
44748      */
44749     getValue : function(){
44750         if(this.el){
44751             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44752         }
44753         return this.valueOff;
44754         
44755     },
44756
44757         // private
44758     onClick : function(){ 
44759         if (this.disabled) {
44760             return;
44761         }
44762         this.setChecked(!this.checked);
44763
44764         //if(this.el.dom.checked != this.checked){
44765         //    this.setValue(this.el.dom.checked);
44766        // }
44767     },
44768
44769     /**
44770      * Sets the checked state of the checkbox.
44771      * On is always based on a string comparison between inputValue and the param.
44772      * @param {Boolean/String} value - the value to set 
44773      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44774      */
44775     setValue : function(v,suppressEvent){
44776         
44777         
44778         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44779         //if(this.el && this.el.dom){
44780         //    this.el.dom.checked = this.checked;
44781         //    this.el.dom.defaultChecked = this.checked;
44782         //}
44783         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44784         //this.fireEvent("check", this, this.checked);
44785     },
44786     // private..
44787     setChecked : function(state,suppressEvent)
44788     {
44789         if (this.inSetChecked) {
44790             this.checked = state;
44791             return;
44792         }
44793         
44794     
44795         if(this.wrap){
44796             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44797         }
44798         this.checked = state;
44799         if(suppressEvent !== true){
44800             this.fireEvent('check', this, state);
44801         }
44802         this.inSetChecked = true;
44803         this.el.dom.value = state ? this.inputValue : this.valueOff;
44804         this.inSetChecked = false;
44805         
44806     },
44807     // handle setting of hidden value by some other method!!?!?
44808     setFromHidden: function()
44809     {
44810         if(!this.el){
44811             return;
44812         }
44813         //console.log("SET FROM HIDDEN");
44814         //alert('setFrom hidden');
44815         this.setValue(this.el.dom.value);
44816     },
44817     
44818     onDestroy : function()
44819     {
44820         if(this.viewEl){
44821             Roo.get(this.viewEl).remove();
44822         }
44823          
44824         Roo.form.Checkbox.superclass.onDestroy.call(this);
44825     },
44826     
44827     setBoxLabel : function(str)
44828     {
44829         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44830     }
44831
44832 });/*
44833  * Based on:
44834  * Ext JS Library 1.1.1
44835  * Copyright(c) 2006-2007, Ext JS, LLC.
44836  *
44837  * Originally Released Under LGPL - original licence link has changed is not relivant.
44838  *
44839  * Fork - LGPL
44840  * <script type="text/javascript">
44841  */
44842  
44843 /**
44844  * @class Roo.form.Radio
44845  * @extends Roo.form.Checkbox
44846  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44847  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44848  * @constructor
44849  * Creates a new Radio
44850  * @param {Object} config Configuration options
44851  */
44852 Roo.form.Radio = function(){
44853     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44854 };
44855 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44856     inputType: 'radio',
44857
44858     /**
44859      * If this radio is part of a group, it will return the selected value
44860      * @return {String}
44861      */
44862     getGroupValue : function(){
44863         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44864     },
44865     
44866     
44867     onRender : function(ct, position){
44868         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44869         
44870         if(this.inputValue !== undefined){
44871             this.el.dom.value = this.inputValue;
44872         }
44873          
44874         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44875         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44876         //var viewEl = this.wrap.createChild({ 
44877         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44878         //this.viewEl = viewEl;   
44879         //this.wrap.on('click', this.onClick,  this); 
44880         
44881         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44882         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44883         
44884         
44885         
44886         if(this.boxLabel){
44887             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44888         //    viewEl.on('click', this.onClick,  this); 
44889         }
44890          if(this.checked){
44891             this.el.dom.checked =   'checked' ;
44892         }
44893          
44894     } 
44895     
44896     
44897 });Roo.rtf = {}; // namespace
44898 Roo.rtf.Hex = function(hex)
44899 {
44900     this.hexstr = hex;
44901 };
44902 Roo.rtf.Paragraph = function(opts)
44903 {
44904     this.content = []; ///??? is that used?
44905 };Roo.rtf.Span = function(opts)
44906 {
44907     this.value = opts.value;
44908 };
44909
44910 Roo.rtf.Group = function(parent)
44911 {
44912     // we dont want to acutally store parent - it will make debug a nightmare..
44913     this.content = [];
44914     this.cn  = [];
44915      
44916        
44917     
44918 };
44919
44920 Roo.rtf.Group.prototype = {
44921     ignorable : false,
44922     content: false,
44923     cn: false,
44924     addContent : function(node) {
44925         // could set styles...
44926         this.content.push(node);
44927     },
44928     addChild : function(cn)
44929     {
44930         this.cn.push(cn);
44931     },
44932     // only for images really...
44933     toDataURL : function()
44934     {
44935         var mimetype = false;
44936         switch(true) {
44937             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44938                 mimetype = "image/png";
44939                 break;
44940              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44941                 mimetype = "image/jpeg";
44942                 break;
44943             default :
44944                 return 'about:blank'; // ?? error?
44945         }
44946         
44947         
44948         var hexstring = this.content[this.content.length-1].value;
44949         
44950         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44951             return String.fromCharCode(parseInt(a, 16));
44952         }).join(""));
44953     }
44954     
44955 };
44956 // this looks like it's normally the {rtf{ .... }}
44957 Roo.rtf.Document = function()
44958 {
44959     // we dont want to acutally store parent - it will make debug a nightmare..
44960     this.rtlch  = [];
44961     this.content = [];
44962     this.cn = [];
44963     
44964 };
44965 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44966     addChild : function(cn)
44967     {
44968         this.cn.push(cn);
44969         switch(cn.type) {
44970             case 'rtlch': // most content seems to be inside this??
44971             case 'listtext':
44972             case 'shpinst':
44973                 this.rtlch.push(cn);
44974                 return;
44975             default:
44976                 this[cn.type] = cn;
44977         }
44978         
44979     },
44980     
44981     getElementsByType : function(type)
44982     {
44983         var ret =  [];
44984         this._getElementsByType(type, ret, this.cn, 'rtf');
44985         return ret;
44986     },
44987     _getElementsByType : function (type, ret, search_array, path)
44988     {
44989         search_array.forEach(function(n,i) {
44990             if (n.type == type) {
44991                 n.path = path + '/' + n.type + ':' + i;
44992                 ret.push(n);
44993             }
44994             if (n.cn.length > 0) {
44995                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44996             }
44997         },this);
44998     }
44999     
45000 });
45001  
45002 Roo.rtf.Ctrl = function(opts)
45003 {
45004     this.value = opts.value;
45005     this.param = opts.param;
45006 };
45007 /**
45008  *
45009  *
45010  * based on this https://github.com/iarna/rtf-parser
45011  * it's really only designed to extract pict from pasted RTF 
45012  *
45013  * usage:
45014  *
45015  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45016  *  
45017  *
45018  */
45019
45020  
45021
45022
45023
45024 Roo.rtf.Parser = function(text) {
45025     //super({objectMode: true})
45026     this.text = '';
45027     this.parserState = this.parseText;
45028     
45029     // these are for interpeter...
45030     this.doc = {};
45031     ///this.parserState = this.parseTop
45032     this.groupStack = [];
45033     this.hexStore = [];
45034     this.doc = false;
45035     
45036     this.groups = []; // where we put the return.
45037     
45038     for (var ii = 0; ii < text.length; ++ii) {
45039         ++this.cpos;
45040         
45041         if (text[ii] === '\n') {
45042             ++this.row;
45043             this.col = 1;
45044         } else {
45045             ++this.col;
45046         }
45047         this.parserState(text[ii]);
45048     }
45049     
45050     
45051     
45052 };
45053 Roo.rtf.Parser.prototype = {
45054     text : '', // string being parsed..
45055     controlWord : '',
45056     controlWordParam :  '',
45057     hexChar : '',
45058     doc : false,
45059     group: false,
45060     groupStack : false,
45061     hexStore : false,
45062     
45063     
45064     cpos : 0, 
45065     row : 1, // reportin?
45066     col : 1, //
45067
45068      
45069     push : function (el)
45070     {
45071         var m = 'cmd'+ el.type;
45072         if (typeof(this[m]) == 'undefined') {
45073             Roo.log('invalid cmd:' + el.type);
45074             return;
45075         }
45076         this[m](el);
45077         //Roo.log(el);
45078     },
45079     flushHexStore : function()
45080     {
45081         if (this.hexStore.length < 1) {
45082             return;
45083         }
45084         var hexstr = this.hexStore.map(
45085             function(cmd) {
45086                 return cmd.value;
45087         }).join('');
45088         
45089         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45090               
45091             
45092         this.hexStore.splice(0)
45093         
45094     },
45095     
45096     cmdgroupstart : function()
45097     {
45098         this.flushHexStore();
45099         if (this.group) {
45100             this.groupStack.push(this.group);
45101         }
45102          // parent..
45103         if (this.doc === false) {
45104             this.group = this.doc = new Roo.rtf.Document();
45105             return;
45106             
45107         }
45108         this.group = new Roo.rtf.Group(this.group);
45109     },
45110     cmdignorable : function()
45111     {
45112         this.flushHexStore();
45113         this.group.ignorable = true;
45114     },
45115     cmdendparagraph : function()
45116     {
45117         this.flushHexStore();
45118         this.group.addContent(new Roo.rtf.Paragraph());
45119     },
45120     cmdgroupend : function ()
45121     {
45122         this.flushHexStore();
45123         var endingGroup = this.group;
45124         
45125         
45126         this.group = this.groupStack.pop();
45127         if (this.group) {
45128             this.group.addChild(endingGroup);
45129         }
45130         
45131         
45132         
45133         var doc = this.group || this.doc;
45134         //if (endingGroup instanceof FontTable) {
45135         //  doc.fonts = endingGroup.table
45136         //} else if (endingGroup instanceof ColorTable) {
45137         //  doc.colors = endingGroup.table
45138         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45139         if (endingGroup.ignorable === false) {
45140             //code
45141             this.groups.push(endingGroup);
45142            // Roo.log( endingGroup );
45143         }
45144             //Roo.each(endingGroup.content, function(item)) {
45145             //    doc.addContent(item);
45146             //}
45147             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45148         //}
45149     },
45150     cmdtext : function (cmd)
45151     {
45152         this.flushHexStore();
45153         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45154             //this.group = this.doc
45155         }
45156         this.group.addContent(new Roo.rtf.Span(cmd));
45157     },
45158     cmdcontrolword : function (cmd)
45159     {
45160         this.flushHexStore();
45161         if (!this.group.type) {
45162             this.group.type = cmd.value;
45163             return;
45164         }
45165         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45166         // we actually don't care about ctrl words...
45167         return ;
45168         /*
45169         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45170         if (this[method]) {
45171             this[method](cmd.param)
45172         } else {
45173             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45174         }
45175         */
45176     },
45177     cmdhexchar : function(cmd) {
45178         this.hexStore.push(cmd);
45179     },
45180     cmderror : function(cmd) {
45181         throw new Exception (cmd.value);
45182     },
45183     
45184     /*
45185       _flush (done) {
45186         if (this.text !== '\u0000') this.emitText()
45187         done()
45188       }
45189       */
45190       
45191       
45192     parseText : function(c)
45193     {
45194         if (c === '\\') {
45195             this.parserState = this.parseEscapes;
45196         } else if (c === '{') {
45197             this.emitStartGroup();
45198         } else if (c === '}') {
45199             this.emitEndGroup();
45200         } else if (c === '\x0A' || c === '\x0D') {
45201             // cr/lf are noise chars
45202         } else {
45203             this.text += c;
45204         }
45205     },
45206     
45207     parseEscapes: function (c)
45208     {
45209         if (c === '\\' || c === '{' || c === '}') {
45210             this.text += c;
45211             this.parserState = this.parseText;
45212         } else {
45213             this.parserState = this.parseControlSymbol;
45214             this.parseControlSymbol(c);
45215         }
45216     },
45217     parseControlSymbol: function(c)
45218     {
45219         if (c === '~') {
45220             this.text += '\u00a0'; // nbsp
45221             this.parserState = this.parseText
45222         } else if (c === '-') {
45223              this.text += '\u00ad'; // soft hyphen
45224         } else if (c === '_') {
45225             this.text += '\u2011'; // non-breaking hyphen
45226         } else if (c === '*') {
45227             this.emitIgnorable();
45228             this.parserState = this.parseText;
45229         } else if (c === "'") {
45230             this.parserState = this.parseHexChar;
45231         } else if (c === '|') { // formula cacter
45232             this.emitFormula();
45233             this.parserState = this.parseText;
45234         } else if (c === ':') { // subentry in an index entry
45235             this.emitIndexSubEntry();
45236             this.parserState = this.parseText;
45237         } else if (c === '\x0a') {
45238             this.emitEndParagraph();
45239             this.parserState = this.parseText;
45240         } else if (c === '\x0d') {
45241             this.emitEndParagraph();
45242             this.parserState = this.parseText;
45243         } else {
45244             this.parserState = this.parseControlWord;
45245             this.parseControlWord(c);
45246         }
45247     },
45248     parseHexChar: function (c)
45249     {
45250         if (/^[A-Fa-f0-9]$/.test(c)) {
45251             this.hexChar += c;
45252             if (this.hexChar.length >= 2) {
45253               this.emitHexChar();
45254               this.parserState = this.parseText;
45255             }
45256             return;
45257         }
45258         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45259         this.parserState = this.parseText;
45260         
45261     },
45262     parseControlWord : function(c)
45263     {
45264         if (c === ' ') {
45265             this.emitControlWord();
45266             this.parserState = this.parseText;
45267         } else if (/^[-\d]$/.test(c)) {
45268             this.parserState = this.parseControlWordParam;
45269             this.controlWordParam += c;
45270         } else if (/^[A-Za-z]$/.test(c)) {
45271           this.controlWord += c;
45272         } else {
45273           this.emitControlWord();
45274           this.parserState = this.parseText;
45275           this.parseText(c);
45276         }
45277     },
45278     parseControlWordParam : function (c) {
45279         if (/^\d$/.test(c)) {
45280           this.controlWordParam += c;
45281         } else if (c === ' ') {
45282           this.emitControlWord();
45283           this.parserState = this.parseText;
45284         } else {
45285           this.emitControlWord();
45286           this.parserState = this.parseText;
45287           this.parseText(c);
45288         }
45289     },
45290     
45291     
45292     
45293     
45294     emitText : function () {
45295         if (this.text === '') {
45296             return;
45297         }
45298         this.push({
45299             type: 'text',
45300             value: this.text,
45301             pos: this.cpos,
45302             row: this.row,
45303             col: this.col
45304         });
45305         this.text = ''
45306     },
45307     emitControlWord : function ()
45308     {
45309         this.emitText();
45310         if (this.controlWord === '') {
45311             this.emitError('empty control word');
45312         } else {
45313             this.push({
45314                   type: 'controlword',
45315                   value: this.controlWord,
45316                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45317                   pos: this.cpos,
45318                   row: this.row,
45319                   col: this.col
45320             });
45321         }
45322         this.controlWord = '';
45323         this.controlWordParam = '';
45324     },
45325     emitStartGroup : function ()
45326     {
45327         this.emitText();
45328         this.push({
45329             type: 'groupstart',
45330             pos: this.cpos,
45331             row: this.row,
45332             col: this.col
45333         });
45334     },
45335     emitEndGroup : function ()
45336     {
45337         this.emitText();
45338         this.push({
45339             type: 'groupend',
45340             pos: this.cpos,
45341             row: this.row,
45342             col: this.col
45343         });
45344     },
45345     emitIgnorable : function ()
45346     {
45347         this.emitText();
45348         this.push({
45349             type: 'ignorable',
45350             pos: this.cpos,
45351             row: this.row,
45352             col: this.col
45353         });
45354     },
45355     emitHexChar : function ()
45356     {
45357         this.emitText();
45358         this.push({
45359             type: 'hexchar',
45360             value: this.hexChar,
45361             pos: this.cpos,
45362             row: this.row,
45363             col: this.col
45364         });
45365         this.hexChar = ''
45366     },
45367     emitError : function (message)
45368     {
45369       this.emitText();
45370       this.push({
45371             type: 'error',
45372             value: message,
45373             row: this.row,
45374             col: this.col,
45375             char: this.cpos //,
45376             //stack: new Error().stack
45377         });
45378     },
45379     emitEndParagraph : function () {
45380         this.emitText();
45381         this.push({
45382             type: 'endparagraph',
45383             pos: this.cpos,
45384             row: this.row,
45385             col: this.col
45386         });
45387     }
45388      
45389 } ;
45390 Roo.htmleditor = {};
45391  
45392 /**
45393  * @class Roo.htmleditor.Filter
45394  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45395  * @cfg {DomElement} node The node to iterate and filter
45396  * @cfg {boolean|String|Array} tag Tags to replace 
45397  * @constructor
45398  * Create a new Filter.
45399  * @param {Object} config Configuration options
45400  */
45401
45402
45403
45404 Roo.htmleditor.Filter = function(cfg) {
45405     Roo.apply(this.cfg);
45406     // this does not actually call walk as it's really just a abstract class
45407 }
45408
45409
45410 Roo.htmleditor.Filter.prototype = {
45411     
45412     node: false,
45413     
45414     tag: false,
45415
45416     // overrride to do replace comments.
45417     replaceComment : false,
45418     
45419     // overrride to do replace or do stuff with tags..
45420     replaceTag : false,
45421     
45422     walk : function(dom)
45423     {
45424         Roo.each( Array.from(dom.childNodes), function( e ) {
45425             switch(true) {
45426                 
45427                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45428                     this.replaceComment(e);
45429                     return;
45430                 
45431                 case e.nodeType != 1: //not a node.
45432                     return;
45433                 
45434                 case this.tag === true: // everything
45435                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45436                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45437                     if (this.replaceTag && false === this.replaceTag(e)) {
45438                         return;
45439                     }
45440                     if (e.hasChildNodes()) {
45441                         this.walk(e);
45442                     }
45443                     return;
45444                 
45445                 default:    // tags .. that do not match.
45446                     if (e.hasChildNodes()) {
45447                         this.walk(e);
45448                     }
45449             }
45450             
45451         }, this);
45452         
45453     }
45454 }; 
45455
45456 /**
45457  * @class Roo.htmleditor.FilterAttributes
45458  * clean attributes and  styles including http:// etc.. in attribute
45459  * @constructor
45460 * Run a new Attribute Filter
45461 * @param {Object} config Configuration options
45462  */
45463 Roo.htmleditor.FilterAttributes = function(cfg)
45464 {
45465     Roo.apply(this, cfg);
45466     this.attrib_black = this.attrib_black || [];
45467     this.attrib_white = this.attrib_white || [];
45468
45469     this.attrib_clean = this.attrib_clean || [];
45470     this.style_white = this.style_white || [];
45471     this.style_black = this.style_black || [];
45472     this.walk(cfg.node);
45473 }
45474
45475 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45476 {
45477     tag: true, // all tags
45478     
45479     attrib_black : false, // array
45480     attrib_clean : false,
45481     attrib_white : false,
45482
45483     style_white : false,
45484     style_black : false,
45485      
45486      
45487     replaceTag : function(node)
45488     {
45489         if (!node.attributes || !node.attributes.length) {
45490             return true;
45491         }
45492         
45493         for (var i = node.attributes.length-1; i > -1 ; i--) {
45494             var a = node.attributes[i];
45495             //console.log(a);
45496             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45497                 node.removeAttribute(a.name);
45498                 continue;
45499             }
45500             
45501             
45502             
45503             if (a.name.toLowerCase().substr(0,2)=='on')  {
45504                 node.removeAttribute(a.name);
45505                 continue;
45506             }
45507             
45508             
45509             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45510                 node.removeAttribute(a.name);
45511                 continue;
45512             }
45513             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45514                 this.cleanAttr(node,a.name,a.value); // fixme..
45515                 continue;
45516             }
45517             if (a.name == 'style') {
45518                 this.cleanStyle(node,a.name,a.value);
45519                 continue;
45520             }
45521             /// clean up MS crap..
45522             // tecnically this should be a list of valid class'es..
45523             
45524             
45525             if (a.name == 'class') {
45526                 if (a.value.match(/^Mso/)) {
45527                     node.removeAttribute('class');
45528                 }
45529                 
45530                 if (a.value.match(/^body$/)) {
45531                     node.removeAttribute('class');
45532                 }
45533                 continue;
45534             }
45535             
45536             
45537             // style cleanup!?
45538             // class cleanup?
45539             
45540         }
45541         return true; // clean children
45542     },
45543         
45544     cleanAttr: function(node, n,v)
45545     {
45546         
45547         if (v.match(/^\./) || v.match(/^\//)) {
45548             return;
45549         }
45550         if (v.match(/^(http|https):\/\//)
45551             || v.match(/^mailto:/) 
45552             || v.match(/^ftp:/)
45553             || v.match(/^data:/)
45554             ) {
45555             return;
45556         }
45557         if (v.match(/^#/)) {
45558             return;
45559         }
45560         if (v.match(/^\{/)) { // allow template editing.
45561             return;
45562         }
45563 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45564         node.removeAttribute(n);
45565         
45566     },
45567     cleanStyle : function(node,  n,v)
45568     {
45569         if (v.match(/expression/)) { //XSS?? should we even bother..
45570             node.removeAttribute(n);
45571             return;
45572         }
45573         
45574         var parts = v.split(/;/);
45575         var clean = [];
45576         
45577         Roo.each(parts, function(p) {
45578             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45579             if (!p.length) {
45580                 return true;
45581             }
45582             var l = p.split(':').shift().replace(/\s+/g,'');
45583             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45584             
45585             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45586                 return true;
45587             }
45588             //Roo.log()
45589             // only allow 'c whitelisted system attributes'
45590             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45591                 return true;
45592             }
45593             
45594             
45595             clean.push(p);
45596             return true;
45597         },this);
45598         if (clean.length) { 
45599             node.setAttribute(n, clean.join(';'));
45600         } else {
45601             node.removeAttribute(n);
45602         }
45603         
45604     }
45605         
45606         
45607         
45608     
45609 });/**
45610  * @class Roo.htmleditor.FilterBlack
45611  * remove blacklisted elements.
45612  * @constructor
45613  * Run a new Blacklisted Filter
45614  * @param {Object} config Configuration options
45615  */
45616
45617 Roo.htmleditor.FilterBlack = function(cfg)
45618 {
45619     Roo.apply(this, cfg);
45620     this.walk(cfg.node);
45621 }
45622
45623 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45624 {
45625     tag : true, // all elements.
45626    
45627     replace : function(n)
45628     {
45629         n.parentNode.removeChild(n);
45630     }
45631 });
45632 /**
45633  * @class Roo.htmleditor.FilterComment
45634  * remove comments.
45635  * @constructor
45636 * Run a new Comments Filter
45637 * @param {Object} config Configuration options
45638  */
45639 Roo.htmleditor.FilterComment = function(cfg)
45640 {
45641     this.walk(cfg.node);
45642 }
45643
45644 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45645 {
45646   
45647     replaceComment : function(n)
45648     {
45649         n.parentNode.removeChild(n);
45650     }
45651 });/**
45652  * @class Roo.htmleditor.FilterKeepChildren
45653  * remove tags but keep children
45654  * @constructor
45655  * Run a new Keep Children Filter
45656  * @param {Object} config Configuration options
45657  */
45658
45659 Roo.htmleditor.FilterKeepChildren = function(cfg)
45660 {
45661     Roo.apply(this, cfg);
45662     if (this.tag === false) {
45663         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45664     }
45665     this.walk(cfg.node);
45666 }
45667
45668 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45669 {
45670     
45671   
45672     replaceTag : function(node)
45673     {
45674         // walk children...
45675         //Roo.log(node);
45676         var ar = Array.from(node.childNodes);
45677         //remove first..
45678         for (var i = 0; i < ar.length; i++) {
45679             if (ar[i].nodeType == 1) {
45680                 if (
45681                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45682                     || // array and it matches
45683                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45684                 ) {
45685                     this.replaceTag(ar[i]); // child is blacklisted as well...
45686                     continue;
45687                 }
45688             }
45689         }  
45690         ar = Array.from(node.childNodes);
45691         for (var i = 0; i < ar.length; i++) {
45692          
45693             node.removeChild(ar[i]);
45694             // what if we need to walk these???
45695             node.parentNode.insertBefore(ar[i], node);
45696             if (this.tag !== false) {
45697                 this.walk(ar[i]);
45698                 
45699             }
45700         }
45701         node.parentNode.removeChild(node);
45702         return false; // don't walk children
45703         
45704         
45705     }
45706 });/**
45707  * @class Roo.htmleditor.FilterParagraph
45708  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45709  * like on 'push' to remove the <p> tags and replace them with line breaks.
45710  * @constructor
45711  * Run a new Paragraph Filter
45712  * @param {Object} config Configuration options
45713  */
45714
45715 Roo.htmleditor.FilterParagraph = function(cfg)
45716 {
45717     // no need to apply config.
45718     this.walk(cfg.node);
45719 }
45720
45721 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45722 {
45723     
45724      
45725     tag : 'P',
45726     
45727      
45728     replaceTag : function(node)
45729     {
45730         
45731         if (node.childNodes.length == 1 &&
45732             node.childNodes[0].nodeType == 3 &&
45733             node.childNodes[0].textContent.trim().length < 1
45734             ) {
45735             // remove and replace with '<BR>';
45736             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45737             return false; // no need to walk..
45738         }
45739         var ar = Array.from(node.childNodes);
45740         for (var i = 0; i < ar.length; i++) {
45741             node.removeChild(ar[i]);
45742             // what if we need to walk these???
45743             node.parentNode.insertBefore(ar[i], node);
45744         }
45745         // now what about this?
45746         // <p> &nbsp; </p>
45747         
45748         // double BR.
45749         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45750         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45751         node.parentNode.removeChild(node);
45752         
45753         return false;
45754
45755     }
45756     
45757 });/**
45758  * @class Roo.htmleditor.FilterSpan
45759  * filter span's with no attributes out..
45760  * @constructor
45761  * Run a new Span Filter
45762  * @param {Object} config Configuration options
45763  */
45764
45765 Roo.htmleditor.FilterSpan = function(cfg)
45766 {
45767     // no need to apply config.
45768     this.walk(cfg.node);
45769 }
45770
45771 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45772 {
45773      
45774     tag : 'SPAN',
45775      
45776  
45777     replaceTag : function(node)
45778     {
45779         if (node.attributes && node.attributes.length > 0) {
45780             return true; // walk if there are any.
45781         }
45782         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45783         return false;
45784      
45785     }
45786     
45787 });/**
45788  * @class Roo.htmleditor.FilterTableWidth
45789   try and remove table width data - as that frequently messes up other stuff.
45790  * 
45791  *      was cleanTableWidths.
45792  *
45793  * Quite often pasting from word etc.. results in tables with column and widths.
45794  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45795  *
45796  * @constructor
45797  * Run a new Table Filter
45798  * @param {Object} config Configuration options
45799  */
45800
45801 Roo.htmleditor.FilterTableWidth = function(cfg)
45802 {
45803     // no need to apply config.
45804     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45805     this.walk(cfg.node);
45806 }
45807
45808 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45809 {
45810      
45811      
45812     
45813     replaceTag: function(node) {
45814         
45815         
45816       
45817         if (node.hasAttribute('width')) {
45818             node.removeAttribute('width');
45819         }
45820         
45821          
45822         if (node.hasAttribute("style")) {
45823             // pretty basic...
45824             
45825             var styles = node.getAttribute("style").split(";");
45826             var nstyle = [];
45827             Roo.each(styles, function(s) {
45828                 if (!s.match(/:/)) {
45829                     return;
45830                 }
45831                 var kv = s.split(":");
45832                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45833                     return;
45834                 }
45835                 // what ever is left... we allow.
45836                 nstyle.push(s);
45837             });
45838             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45839             if (!nstyle.length) {
45840                 node.removeAttribute('style');
45841             }
45842         }
45843         
45844         return true; // continue doing children..
45845     }
45846 });/**
45847  * @class Roo.htmleditor.FilterWord
45848  * try and clean up all the mess that Word generates.
45849  * 
45850  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45851  
45852  * @constructor
45853  * Run a new Span Filter
45854  * @param {Object} config Configuration options
45855  */
45856
45857 Roo.htmleditor.FilterWord = function(cfg)
45858 {
45859     // no need to apply config.
45860     this.walk(cfg.node);
45861 }
45862
45863 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45864 {
45865     tag: true,
45866      
45867     
45868     /**
45869      * Clean up MS wordisms...
45870      */
45871     replaceTag : function(node)
45872     {
45873          
45874         // no idea what this does - span with text, replaceds with just text.
45875         if(
45876                 node.nodeName == 'SPAN' &&
45877                 !node.hasAttributes() &&
45878                 node.childNodes.length == 1 &&
45879                 node.firstChild.nodeName == "#text"  
45880         ) {
45881             var textNode = node.firstChild;
45882             node.removeChild(textNode);
45883             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45884                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45885             }
45886             node.parentNode.insertBefore(textNode, node);
45887             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45888                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45889             }
45890             
45891             node.parentNode.removeChild(node);
45892             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45893         }
45894         
45895    
45896         
45897         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45898             node.parentNode.removeChild(node);
45899             return false; // dont do chidlren
45900         }
45901         //Roo.log(node.tagName);
45902         // remove - but keep children..
45903         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45904             //Roo.log('-- removed');
45905             while (node.childNodes.length) {
45906                 var cn = node.childNodes[0];
45907                 node.removeChild(cn);
45908                 node.parentNode.insertBefore(cn, node);
45909                 // move node to parent - and clean it..
45910                 this.replaceTag(cn);
45911             }
45912             node.parentNode.removeChild(node);
45913             /// no need to iterate chidlren = it's got none..
45914             //this.iterateChildren(node, this.cleanWord);
45915             return false; // no need to iterate children.
45916         }
45917         // clean styles
45918         if (node.className.length) {
45919             
45920             var cn = node.className.split(/\W+/);
45921             var cna = [];
45922             Roo.each(cn, function(cls) {
45923                 if (cls.match(/Mso[a-zA-Z]+/)) {
45924                     return;
45925                 }
45926                 cna.push(cls);
45927             });
45928             node.className = cna.length ? cna.join(' ') : '';
45929             if (!cna.length) {
45930                 node.removeAttribute("class");
45931             }
45932         }
45933         
45934         if (node.hasAttribute("lang")) {
45935             node.removeAttribute("lang");
45936         }
45937         
45938         if (node.hasAttribute("style")) {
45939             
45940             var styles = node.getAttribute("style").split(";");
45941             var nstyle = [];
45942             Roo.each(styles, function(s) {
45943                 if (!s.match(/:/)) {
45944                     return;
45945                 }
45946                 var kv = s.split(":");
45947                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45948                     return;
45949                 }
45950                 // what ever is left... we allow.
45951                 nstyle.push(s);
45952             });
45953             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45954             if (!nstyle.length) {
45955                 node.removeAttribute('style');
45956             }
45957         }
45958         return true; // do children
45959         
45960         
45961         
45962     }
45963 });
45964 /**
45965  * @class Roo.htmleditor.FilterStyleToTag
45966  * part of the word stuff... - certain 'styles' should be converted to tags.
45967  * eg.
45968  *   font-weight: bold -> bold
45969  *   ?? super / subscrit etc..
45970  * 
45971  * @constructor
45972 * Run a new style to tag filter.
45973 * @param {Object} config Configuration options
45974  */
45975 Roo.htmleditor.FilterStyleToTag = function(cfg)
45976 {
45977     
45978     this.tags = {
45979         B  : [ 'fontWeight' , 'bold'],
45980         I :  [ 'fontStyle' , 'italic'],
45981         //pre :  [ 'font-style' , 'italic'],
45982         // h1.. h6 ?? font-size?
45983         SUP : [ 'verticalAlign' , 'super' ],
45984         SUB : [ 'verticalAlign' , 'sub' ]
45985         
45986         
45987     };
45988     
45989     Roo.apply(this, cfg);
45990      
45991     
45992     this.walk(cfg.node);
45993     
45994     
45995     
45996 }
45997
45998
45999 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
46000 {
46001     tag: true, // all tags
46002     
46003     tags : false,
46004     
46005     
46006     replaceTag : function(node)
46007     {
46008         
46009         
46010         if (node.getAttribute("style") === null) {
46011             return true;
46012         }
46013         var inject = [];
46014         for (var k in this.tags) {
46015             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46016                 inject.push(k);
46017                 node.style.removeProperty(this.tags[k][0]);
46018             }
46019         }
46020         if (!inject.length) {
46021             return true; 
46022         }
46023         var cn = Array.from(node.childNodes);
46024         var nn = node;
46025         Roo.each(inject, function(t) {
46026             var nc = node.ownerDocument.createelement(t);
46027             nn.appendChild(nc);
46028             nn = nc;
46029         });
46030         for(var i = 0;i < cn.length;cn++) {
46031             node.removeChild(cn[i]);
46032             nn.appendChild(cn[i]);
46033         }
46034         return true /// iterate thru
46035     }
46036     
46037 })/**
46038  * @class Roo.htmleditor.FilterLongBr
46039  * BR/BR/BR - keep a maximum of 2...
46040  * @constructor
46041  * Run a new Long BR Filter
46042  * @param {Object} config Configuration options
46043  */
46044
46045 Roo.htmleditor.FilterLongBr = function(cfg)
46046 {
46047     // no need to apply config.
46048     this.walk(cfg.node);
46049 }
46050
46051 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46052 {
46053     
46054      
46055     tag : 'BR',
46056     
46057      
46058     replaceTag : function(node)
46059     {
46060         
46061         var ps = node.nextSibling;
46062         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46063             ps = ps.nextSibling;
46064         }
46065         
46066         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46067             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46068             return false;
46069         }
46070         
46071         if (!ps || ps.nodeType != 1) {
46072             return false;
46073         }
46074         
46075         if (!ps || ps.tagName != 'BR') {
46076            
46077             return false;
46078         }
46079         
46080         
46081         
46082         
46083         
46084         if (!node.previousSibling) {
46085             return false;
46086         }
46087         var ps = node.previousSibling;
46088         
46089         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46090             ps = ps.previousSibling;
46091         }
46092         if (!ps || ps.nodeType != 1) {
46093             return false;
46094         }
46095         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46096         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46097             return false;
46098         }
46099         
46100         node.parentNode.removeChild(node); // remove me...
46101         
46102         return false; // no need to do children
46103
46104     }
46105     
46106 });
46107 /**
46108  * @class Roo.htmleditor.Tidy
46109  * Tidy HTML 
46110  * @cfg {Roo.HtmlEditorCore} core the editor.
46111  * @constructor
46112  * Create a new Filter.
46113  * @param {Object} config Configuration options
46114  */
46115
46116
46117 Roo.htmleditor.Tidy = function(cfg) {
46118     Roo.apply(this, cfg);
46119     
46120     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46121      
46122 }
46123
46124 Roo.htmleditor.Tidy.toString = function(node)
46125 {
46126     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46127 }
46128
46129 Roo.htmleditor.Tidy.prototype = {
46130     
46131     
46132     wrap : function(s) {
46133         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46134     },
46135
46136     
46137     tidy : function(node, indent) {
46138      
46139         if  (node.nodeType == 3) {
46140             // text.
46141             
46142             
46143             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46144                 
46145             
46146         }
46147         
46148         if  (node.nodeType != 1) {
46149             return '';
46150         }
46151         
46152         
46153         
46154         if (node.tagName == 'BODY') {
46155             
46156             return this.cn(node, '');
46157         }
46158              
46159              // Prints the node tagName, such as <A>, <IMG>, etc
46160         var ret = "<" + node.tagName +  this.attr(node) ;
46161         
46162         // elements with no children..
46163         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46164                 return ret + '/>';
46165         }
46166         ret += '>';
46167         
46168         
46169         var cindent = indent === false ? '' : (indent + '  ');
46170         // tags where we will not pad the children.. (inline text tags etc..)
46171         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46172             cindent = false;
46173             
46174             
46175         }
46176         
46177         var cn = this.cn(node, cindent );
46178         
46179         return ret + cn  + '</' + node.tagName + '>';
46180         
46181     },
46182     cn: function(node, indent)
46183     {
46184         var ret = [];
46185         
46186         var ar = Array.from(node.childNodes);
46187         for (var i = 0 ; i < ar.length ; i++) {
46188             
46189             
46190             
46191             if (indent !== false   // indent==false preservies everything
46192                 && i > 0
46193                 && ar[i].nodeType == 3 
46194                 && ar[i].nodeValue.length > 0
46195                 && ar[i].nodeValue.match(/^\s+/)
46196             ) {
46197                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46198                     ret.pop(); // remove line break from last?
46199                 }
46200                 
46201                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46202             }
46203             if (indent !== false
46204                 && ar[i].nodeType == 1 // element - and indent is not set... 
46205             ) {
46206                 ret.push("\n" + indent); 
46207             }
46208             
46209             ret.push(this.tidy(ar[i], indent));
46210             // text + trailing indent 
46211             if (indent !== false
46212                 && ar[i].nodeType == 3
46213                 && ar[i].nodeValue.length > 0
46214                 && ar[i].nodeValue.match(/\s+$/)
46215             ){
46216                 ret.push("\n" + indent); 
46217             }
46218             
46219             
46220             
46221             
46222         }
46223         // what if all text?
46224         
46225         
46226         return ret.join('');
46227     },
46228     
46229          
46230         
46231     attr : function(node)
46232     {
46233         var attr = [];
46234         for(i = 0; i < node.attributes.length;i++) {
46235             
46236             // skip empty values?
46237             if (!node.attributes.item(i).value.length) {
46238                 continue;
46239             }
46240             attr.push(  node.attributes.item(i).name + '="' +
46241                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46242             );
46243         }
46244         return attr.length ? (' ' + attr.join(' ') ) : '';
46245         
46246     }
46247     
46248     
46249     
46250 }
46251 /**
46252  * @class Roo.htmleditor.KeyEnter
46253  * Handle Enter press..
46254  * @cfg {Roo.HtmlEditorCore} core the editor.
46255  * @constructor
46256  * Create a new Filter.
46257  * @param {Object} config Configuration options
46258  */
46259
46260
46261
46262 Roo.htmleditor.KeyEnter = function(cfg) {
46263     Roo.apply(this, cfg);
46264     // this does not actually call walk as it's really just a abstract class
46265  
46266     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46267 }
46268
46269
46270 Roo.htmleditor.KeyEnter.prototype = {
46271     
46272     core : false,
46273     
46274     keypress : function(e)
46275     {
46276         if (e.charCode != 13) {
46277             return true;
46278         }
46279         e.preventDefault();
46280         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46281         var doc = this.core.doc;
46282         
46283         var docFragment = doc.createDocumentFragment();
46284     
46285         //add a new line
46286         var newEle = doc.createTextNode('\n');
46287         docFragment.appendChild(newEle);
46288     
46289     
46290         var range = this.core.win.getSelection().getRangeAt(0);
46291         var n = range.commonAncestorContainer ;
46292         while (n && n.nodeType != 1) {
46293             n  = n.parentNode;
46294         }
46295         var li = false;
46296         if (n && n.tagName == 'UL') {
46297             li = doc.createElement('LI');
46298             n.appendChild(li);
46299             
46300         }
46301         if (n && n.tagName == 'LI') {
46302             li = doc.createElement('LI');
46303             if (n.nextSibling) {
46304                 n.parentNode.insertBefore(li, n.firstSibling);
46305                 
46306             } else {
46307                 n.parentNode.appendChild(li);
46308             }
46309         }
46310         if (li) {   
46311             range = doc.createRange();
46312             range.setStartAfter(li);
46313             range.collapse(true);
46314         
46315             //make the cursor there
46316             var sel = this.core.win.getSelection();
46317             sel.removeAllRanges();
46318             sel.addRange(range);
46319             return false;
46320             
46321             
46322         }
46323         //add the br, or p, or something else
46324         newEle = doc.createElement('br');
46325         docFragment.appendChild(newEle);
46326     
46327         //make the br replace selection
46328         
46329         range.deleteContents();
46330         
46331         range.insertNode(docFragment);
46332         range = range.cloneRange();
46333         range.collapse(true);
46334         var sel = this.core.win.getSelection();
46335         sel.removeAllRanges();
46336         sel.addRange(range);
46337         sel.collapseToEnd();
46338     
46339         return false;
46340          
46341     }
46342 };
46343      
46344 /**
46345  * @class Roo.htmleditor.Block
46346  * Base class for html editor blocks - do not use it directly .. extend it..
46347  * @cfg {DomElement} node The node to apply stuff to.
46348  * @cfg {String} friendly_name the name that appears in the context bar about this block
46349  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46350  
46351  * @constructor
46352  * Create a new Filter.
46353  * @param {Object} config Configuration options
46354  */
46355
46356 Roo.htmleditor.Block  = function(cfg)
46357 {
46358     // do nothing .. should not be called really.
46359 }
46360
46361 Roo.htmleditor.Block.factory = function(node)
46362 {
46363     
46364     var id = Roo.get(node).id;
46365     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46366         Roo.htmleditor.Block.cache[id].readElement();
46367         return Roo.htmleditor.Block.cache[id];
46368     }
46369     
46370     var cls = Roo.htmleditor['Block' + node.getAttribute('data-block')];
46371     if (typeof(cls) == 'undefined') {
46372         Roo.log("OOps missing block : " + 'Block' + node.getAttribute('data-block'));
46373         return false;
46374     }
46375     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46376     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46377 };
46378 // question goes here... do we need to clear out this cache sometimes?
46379 // or show we make it relivant to the htmleditor.
46380 Roo.htmleditor.Block.cache = {};
46381
46382 Roo.htmleditor.Block.prototype = {
46383     
46384     node : false,
46385     
46386      // used by context menu
46387     friendly_name : 'Image with caption',
46388     
46389     context : false,
46390     /**
46391      * Update a node with values from this object
46392      * @param {DomElement} node
46393      */
46394     updateElement : function(node)
46395     {
46396         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46397     },
46398      /**
46399      * convert to plain HTML for calling insertAtCursor..
46400      */
46401     toHTML : function()
46402     {
46403         return Roo.DomHelper.markup(this.toObject());
46404     },
46405     /**
46406      * used by readEleemnt to extract data from a node
46407      * may need improving as it's pretty basic
46408      
46409      * @param {DomElement} node
46410      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46411      * @param {String} attribute (use html - for contents, or style for using next param as style)
46412      * @param {String} style the style property - eg. text-align
46413      */
46414     getVal : function(node, tag, attr, style)
46415     {
46416         var n = node;
46417         if (tag !== true && n.tagName != tag.toUpperCase()) {
46418             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46419             // but kiss for now.
46420             n = node.getElementsByTagName(tag).item(0);
46421         }
46422         if (attr == 'html') {
46423             return n.innerHTML;
46424         }
46425         if (attr == 'style') {
46426             return n.style[style]
46427         }
46428         
46429         return Roo.get(n).attr(attr);
46430             
46431     },
46432     /**
46433      * create a DomHelper friendly object - for use with 
46434      * Roo.DomHelper.markup / overwrite / etc..
46435      * (override this)
46436      */
46437     toObject : function()
46438     {
46439         return {};
46440     },
46441       /**
46442      * Read a node that has a 'data-block' property - and extract the values from it.
46443      * @param {DomElement} node - the node
46444      */
46445     readElement : function(node)
46446     {
46447         
46448     } 
46449     
46450     
46451 };
46452
46453  
46454
46455 /**
46456  * @class Roo.htmleditor.BlockFigure
46457  * Block that has an image and a figcaption
46458  * @cfg {String} image_src the url for the image
46459  * @cfg {String} align (left|right) alignment for the block default left
46460  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46461  * @cfg {String} caption the text to appear below  (and in the alt tag)
46462  * @cfg {String|number} image_width the width of the image number or %?
46463  * @cfg {String|number} image_height the height of the image number or %?
46464  * 
46465  * @constructor
46466  * Create a new Filter.
46467  * @param {Object} config Configuration options
46468  */
46469
46470 Roo.htmleditor.BlockFigure = function(cfg)
46471 {
46472     if (cfg.node) {
46473         this.readElement(cfg.node);
46474         this.updateElement(cfg.node);
46475     }
46476     Roo.apply(this, cfg);
46477 }
46478 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46479  
46480     
46481     // setable values.
46482     image_src: '',
46483     
46484     align: 'left',
46485     caption : '',
46486     text_align: 'left',
46487     
46488     width : '46%',
46489     margin: '2%',
46490     
46491     // used by context menu
46492     friendly_name : 'Image with caption',
46493     
46494     context : { // ?? static really
46495         width : {
46496             title: "Width",
46497             width: 40
46498             // ?? number
46499         },
46500         margin : {
46501             title: "Margin",
46502             width: 40
46503             // ?? number
46504         },
46505         align: {
46506             title: "Align",
46507             opts : [[ "left"],[ "right"]],
46508             width : 80
46509             
46510         },
46511         text_align: {
46512             title: "Caption Align",
46513             opts : [ [ "left"],[ "right"],[ "center"]],
46514             width : 80
46515         },
46516         
46517        
46518         image_src : {
46519             title: "Src",
46520             width: 220
46521         }
46522     },
46523     /**
46524      * create a DomHelper friendly object - for use with
46525      * Roo.DomHelper.markup / overwrite / etc..
46526      */
46527     toObject : function()
46528     {
46529         var d = document.createElement('div');
46530         d.innerHTML = this.caption;
46531         
46532         return {
46533             tag: 'figure',
46534             'data-block' : 'Figure',
46535             contenteditable : 'false',
46536             style : {
46537                 display: 'table',
46538                 float :  this.align ,
46539                 width :  this.width,
46540                 margin:  this.margin
46541             },
46542             cn : [
46543                 {
46544                     tag : 'img',
46545                     src : this.image_src,
46546                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46547                     style: {
46548                         width: '100%'
46549                     }
46550                 },
46551                 {
46552                     tag: 'figcaption',
46553                     contenteditable : true,
46554                     style : {
46555                         'text-align': this.text_align
46556                     },
46557                     html : this.caption
46558                     
46559                 }
46560             ]
46561         };
46562     },
46563     
46564     readElement : function(node)
46565     {
46566         this.image_src = this.getVal(node, 'img', 'src');
46567         this.align = this.getVal(node, 'figure', 'style', 'float');
46568         this.caption = this.getVal(node, 'figcaption', 'html');
46569         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46570         this.width = this.getVal(node, 'figure', 'style', 'width');
46571         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46572         
46573     } 
46574     
46575   
46576    
46577      
46578     
46579     
46580     
46581     
46582 })
46583
46584 //<script type="text/javascript">
46585
46586 /*
46587  * Based  Ext JS Library 1.1.1
46588  * Copyright(c) 2006-2007, Ext JS, LLC.
46589  * LGPL
46590  *
46591  */
46592  
46593 /**
46594  * @class Roo.HtmlEditorCore
46595  * @extends Roo.Component
46596  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46597  *
46598  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46599  */
46600
46601 Roo.HtmlEditorCore = function(config){
46602     
46603     
46604     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46605     
46606     
46607     this.addEvents({
46608         /**
46609          * @event initialize
46610          * Fires when the editor is fully initialized (including the iframe)
46611          * @param {Roo.HtmlEditorCore} this
46612          */
46613         initialize: true,
46614         /**
46615          * @event activate
46616          * Fires when the editor is first receives the focus. Any insertion must wait
46617          * until after this event.
46618          * @param {Roo.HtmlEditorCore} this
46619          */
46620         activate: true,
46621          /**
46622          * @event beforesync
46623          * Fires before the textarea is updated with content from the editor iframe. Return false
46624          * to cancel the sync.
46625          * @param {Roo.HtmlEditorCore} this
46626          * @param {String} html
46627          */
46628         beforesync: true,
46629          /**
46630          * @event beforepush
46631          * Fires before the iframe editor is updated with content from the textarea. Return false
46632          * to cancel the push.
46633          * @param {Roo.HtmlEditorCore} this
46634          * @param {String} html
46635          */
46636         beforepush: true,
46637          /**
46638          * @event sync
46639          * Fires when the textarea is updated with content from the editor iframe.
46640          * @param {Roo.HtmlEditorCore} this
46641          * @param {String} html
46642          */
46643         sync: true,
46644          /**
46645          * @event push
46646          * Fires when the iframe editor is updated with content from the textarea.
46647          * @param {Roo.HtmlEditorCore} this
46648          * @param {String} html
46649          */
46650         push: true,
46651         
46652         /**
46653          * @event editorevent
46654          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46655          * @param {Roo.HtmlEditorCore} this
46656          */
46657         editorevent: true
46658         
46659     });
46660     
46661     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46662     
46663     // defaults : white / black...
46664     this.applyBlacklists();
46665     
46666     
46667     
46668 };
46669
46670
46671 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46672
46673
46674      /**
46675      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46676      */
46677     
46678     owner : false,
46679     
46680      /**
46681      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46682      *                        Roo.resizable.
46683      */
46684     resizable : false,
46685      /**
46686      * @cfg {Number} height (in pixels)
46687      */   
46688     height: 300,
46689    /**
46690      * @cfg {Number} width (in pixels)
46691      */   
46692     width: 500,
46693     
46694     /**
46695      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46696      * 
46697      */
46698     stylesheets: false,
46699     
46700     /**
46701      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46702      */
46703     allowComments: false,
46704     // id of frame..
46705     frameId: false,
46706     
46707     // private properties
46708     validationEvent : false,
46709     deferHeight: true,
46710     initialized : false,
46711     activated : false,
46712     sourceEditMode : false,
46713     onFocus : Roo.emptyFn,
46714     iframePad:3,
46715     hideMode:'offsets',
46716     
46717     clearUp: true,
46718     
46719     // blacklist + whitelisted elements..
46720     black: false,
46721     white: false,
46722      
46723     bodyCls : '',
46724
46725     
46726     undoManager : false,
46727     /**
46728      * Protected method that will not generally be called directly. It
46729      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46730      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46731      */
46732     getDocMarkup : function(){
46733         // body styles..
46734         var st = '';
46735         
46736         // inherit styels from page...?? 
46737         if (this.stylesheets === false) {
46738             
46739             Roo.get(document.head).select('style').each(function(node) {
46740                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46741             });
46742             
46743             Roo.get(document.head).select('link').each(function(node) { 
46744                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46745             });
46746             
46747         } else if (!this.stylesheets.length) {
46748                 // simple..
46749                 st = '<style type="text/css">' +
46750                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46751                    '</style>';
46752         } else {
46753             for (var i in this.stylesheets) {
46754                 if (typeof(this.stylesheets[i]) != 'string') {
46755                     continue;
46756                 }
46757                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46758             }
46759             
46760         }
46761         
46762         st +=  '<style type="text/css">' +
46763             'IMG { cursor: pointer } ' +
46764         '</style>';
46765
46766         var cls = 'roo-htmleditor-body';
46767         
46768         if(this.bodyCls.length){
46769             cls += ' ' + this.bodyCls;
46770         }
46771         
46772         return '<html><head>' + st  +
46773             //<style type="text/css">' +
46774             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46775             //'</style>' +
46776             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46777     },
46778
46779     // private
46780     onRender : function(ct, position)
46781     {
46782         var _t = this;
46783         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46784         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46785         
46786         
46787         this.el.dom.style.border = '0 none';
46788         this.el.dom.setAttribute('tabIndex', -1);
46789         this.el.addClass('x-hidden hide');
46790         
46791         
46792         
46793         if(Roo.isIE){ // fix IE 1px bogus margin
46794             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46795         }
46796        
46797         
46798         this.frameId = Roo.id();
46799         
46800          
46801         
46802         var iframe = this.owner.wrap.createChild({
46803             tag: 'iframe',
46804             cls: 'form-control', // bootstrap..
46805             id: this.frameId,
46806             name: this.frameId,
46807             frameBorder : 'no',
46808             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46809         }, this.el
46810         );
46811         
46812         
46813         this.iframe = iframe.dom;
46814
46815         this.assignDocWin();
46816         
46817         this.doc.designMode = 'on';
46818        
46819         this.doc.open();
46820         this.doc.write(this.getDocMarkup());
46821         this.doc.close();
46822
46823         
46824         var task = { // must defer to wait for browser to be ready
46825             run : function(){
46826                 //console.log("run task?" + this.doc.readyState);
46827                 this.assignDocWin();
46828                 if(this.doc.body || this.doc.readyState == 'complete'){
46829                     try {
46830                         this.doc.designMode="on";
46831                         
46832                     } catch (e) {
46833                         return;
46834                     }
46835                     Roo.TaskMgr.stop(task);
46836                     this.initEditor.defer(10, this);
46837                 }
46838             },
46839             interval : 10,
46840             duration: 10000,
46841             scope: this
46842         };
46843         Roo.TaskMgr.start(task);
46844
46845     },
46846
46847     // private
46848     onResize : function(w, h)
46849     {
46850          Roo.log('resize: ' +w + ',' + h );
46851         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46852         if(!this.iframe){
46853             return;
46854         }
46855         if(typeof w == 'number'){
46856             
46857             this.iframe.style.width = w + 'px';
46858         }
46859         if(typeof h == 'number'){
46860             
46861             this.iframe.style.height = h + 'px';
46862             if(this.doc){
46863                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46864             }
46865         }
46866         
46867     },
46868
46869     /**
46870      * Toggles the editor between standard and source edit mode.
46871      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46872      */
46873     toggleSourceEdit : function(sourceEditMode){
46874         
46875         this.sourceEditMode = sourceEditMode === true;
46876         
46877         if(this.sourceEditMode){
46878  
46879             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46880             
46881         }else{
46882             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46883             //this.iframe.className = '';
46884             this.deferFocus();
46885         }
46886         //this.setSize(this.owner.wrap.getSize());
46887         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46888     },
46889
46890     
46891   
46892
46893     /**
46894      * Protected method that will not generally be called directly. If you need/want
46895      * custom HTML cleanup, this is the method you should override.
46896      * @param {String} html The HTML to be cleaned
46897      * return {String} The cleaned HTML
46898      */
46899     cleanHtml : function(html){
46900         html = String(html);
46901         if(html.length > 5){
46902             if(Roo.isSafari){ // strip safari nonsense
46903                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46904             }
46905         }
46906         if(html == '&nbsp;'){
46907             html = '';
46908         }
46909         return html;
46910     },
46911
46912     /**
46913      * HTML Editor -> Textarea
46914      * Protected method that will not generally be called directly. Syncs the contents
46915      * of the editor iframe with the textarea.
46916      */
46917     syncValue : function()
46918     {
46919         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46920         if(this.initialized){
46921             
46922             this.undoManager.addEvent();
46923
46924             
46925             var bd = (this.doc.body || this.doc.documentElement);
46926             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46927             
46928             // not sure if this is really the place for this
46929             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46930             // this has to update attributes that get duped.. like alt and caption..
46931             
46932             
46933             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46934             //     Roo.htmleditor.Block.factory(e);
46935             //},this);
46936             
46937             
46938             var div = document.createElement('div');
46939             div.innerHTML = bd.innerHTML;
46940             // remove content editable. (blocks)
46941             
46942            
46943             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46944             //?? tidy?
46945             var html = div.innerHTML;
46946             if(Roo.isSafari){
46947                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46948                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46949                 if(m && m[1]){
46950                     html = '<div style="'+m[0]+'">' + html + '</div>';
46951                 }
46952             }
46953             html = this.cleanHtml(html);
46954             // fix up the special chars.. normaly like back quotes in word...
46955             // however we do not want to do this with chinese..
46956             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46957                 
46958                 var cc = match.charCodeAt();
46959
46960                 // Get the character value, handling surrogate pairs
46961                 if (match.length == 2) {
46962                     // It's a surrogate pair, calculate the Unicode code point
46963                     var high = match.charCodeAt(0) - 0xD800;
46964                     var low  = match.charCodeAt(1) - 0xDC00;
46965                     cc = (high * 0x400) + low + 0x10000;
46966                 }  else if (
46967                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46968                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46969                     (cc >= 0xf900 && cc < 0xfb00 )
46970                 ) {
46971                         return match;
46972                 }  
46973          
46974                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46975                 return "&#" + cc + ";";
46976                 
46977                 
46978             });
46979             
46980             
46981              
46982             if(this.owner.fireEvent('beforesync', this, html) !== false){
46983                 this.el.dom.value = html;
46984                 this.owner.fireEvent('sync', this, html);
46985             }
46986         }
46987     },
46988
46989     /**
46990      * TEXTAREA -> EDITABLE
46991      * Protected method that will not generally be called directly. Pushes the value of the textarea
46992      * into the iframe editor.
46993      */
46994     pushValue : function()
46995     {
46996         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46997         if(this.initialized){
46998             var v = this.el.dom.value.trim();
46999             
47000             
47001             if(this.owner.fireEvent('beforepush', this, v) !== false){
47002                 var d = (this.doc.body || this.doc.documentElement);
47003                 d.innerHTML = v;
47004                  
47005                 this.el.dom.value = d.innerHTML;
47006                 this.owner.fireEvent('push', this, v);
47007             }
47008             
47009             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
47010                 
47011                 Roo.htmleditor.Block.factory(e);
47012                 
47013             },this);
47014             var lc = this.doc.body.lastChild;
47015             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
47016                 // add an extra line at the end.
47017                 this.doc.body.appendChild(this.doc.createElement('br'));
47018             }
47019             
47020             
47021         }
47022     },
47023
47024     // private
47025     deferFocus : function(){
47026         this.focus.defer(10, this);
47027     },
47028
47029     // doc'ed in Field
47030     focus : function(){
47031         if(this.win && !this.sourceEditMode){
47032             this.win.focus();
47033         }else{
47034             this.el.focus();
47035         }
47036     },
47037     
47038     assignDocWin: function()
47039     {
47040         var iframe = this.iframe;
47041         
47042          if(Roo.isIE){
47043             this.doc = iframe.contentWindow.document;
47044             this.win = iframe.contentWindow;
47045         } else {
47046 //            if (!Roo.get(this.frameId)) {
47047 //                return;
47048 //            }
47049 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47050 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47051             
47052             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47053                 return;
47054             }
47055             
47056             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47057             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47058         }
47059     },
47060     
47061     // private
47062     initEditor : function(){
47063         //console.log("INIT EDITOR");
47064         this.assignDocWin();
47065         
47066         
47067         
47068         this.doc.designMode="on";
47069         this.doc.open();
47070         this.doc.write(this.getDocMarkup());
47071         this.doc.close();
47072         
47073         var dbody = (this.doc.body || this.doc.documentElement);
47074         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47075         // this copies styles from the containing element into thsi one..
47076         // not sure why we need all of this..
47077         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47078         
47079         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47080         //ss['background-attachment'] = 'fixed'; // w3c
47081         dbody.bgProperties = 'fixed'; // ie
47082         //Roo.DomHelper.applyStyles(dbody, ss);
47083         Roo.EventManager.on(this.doc, {
47084             //'mousedown': this.onEditorEvent,
47085             'mouseup': this.onEditorEvent,
47086             'dblclick': this.onEditorEvent,
47087             'click': this.onEditorEvent,
47088             'keyup': this.onEditorEvent,
47089             
47090             buffer:100,
47091             scope: this
47092         });
47093         Roo.EventManager.on(this.doc, {
47094             'paste': this.onPasteEvent,
47095             scope : this
47096         });
47097         if(Roo.isGecko){
47098             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47099         }
47100         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47101             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47102         }
47103         this.initialized = true;
47104
47105         
47106         // initialize special key events - enter
47107         new Roo.htmleditor.KeyEnter({core : this});
47108         
47109          
47110         
47111         this.owner.fireEvent('initialize', this);
47112         this.pushValue();
47113     },
47114     
47115     onPasteEvent : function(e,v)
47116     {
47117         // I think we better assume paste is going to be a dirty load of rubish from word..
47118         
47119         // even pasting into a 'email version' of this widget will have to clean up that mess.
47120         var cd = (e.browserEvent.clipboardData || window.clipboardData);
47121         
47122         // check what type of paste - if it's an image, then handle it differently.
47123         if (cd.files.length > 0) {
47124             // pasting images?
47125             var urlAPI = (window.createObjectURL && window) || 
47126                 (window.URL && URL.revokeObjectURL && URL) || 
47127                 (window.webkitURL && webkitURL);
47128     
47129             var url = urlAPI.createObjectURL( cd.files[0]);
47130             this.insertAtCursor('<img src=" + url + ">');
47131             return false;
47132         }
47133         
47134         var html = cd.getData('text/html'); // clipboard event
47135         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47136         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47137         Roo.log(images);
47138         //Roo.log(imgs);
47139         // fixme..
47140         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47141                        .map(function(g) { return g.toDataURL(); });
47142         
47143         
47144         html = this.cleanWordChars(html);
47145         
47146         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47147         
47148         if (images.length > 0) {
47149             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47150                 img.setAttribute('src', images[i]);
47151             });
47152         }
47153         
47154       
47155         new Roo.htmleditor.FilterStyleToTag({ node : d });
47156         new Roo.htmleditor.FilterAttributes({
47157             node : d,
47158             attrib_white : ['href', 'src', 'name', 'align'],
47159             attrib_clean : ['href', 'src' ] 
47160         });
47161         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47162         // should be fonts..
47163         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47164         new Roo.htmleditor.FilterParagraph({ node : d });
47165         new Roo.htmleditor.FilterSpan({ node : d });
47166         new Roo.htmleditor.FilterLongBr({ node : d });
47167         
47168         
47169         
47170         this.insertAtCursor(d.innerHTML);
47171         
47172         e.preventDefault();
47173         return false;
47174         // default behaveiour should be our local cleanup paste? (optional?)
47175         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47176         //this.owner.fireEvent('paste', e, v);
47177     },
47178     // private
47179     onDestroy : function(){
47180         
47181         
47182         
47183         if(this.rendered){
47184             
47185             //for (var i =0; i < this.toolbars.length;i++) {
47186             //    // fixme - ask toolbars for heights?
47187             //    this.toolbars[i].onDestroy();
47188            // }
47189             
47190             //this.wrap.dom.innerHTML = '';
47191             //this.wrap.remove();
47192         }
47193     },
47194
47195     // private
47196     onFirstFocus : function(){
47197         
47198         this.assignDocWin();
47199         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47200         
47201         this.activated = true;
47202          
47203     
47204         if(Roo.isGecko){ // prevent silly gecko errors
47205             this.win.focus();
47206             var s = this.win.getSelection();
47207             if(!s.focusNode || s.focusNode.nodeType != 3){
47208                 var r = s.getRangeAt(0);
47209                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47210                 r.collapse(true);
47211                 this.deferFocus();
47212             }
47213             try{
47214                 this.execCmd('useCSS', true);
47215                 this.execCmd('styleWithCSS', false);
47216             }catch(e){}
47217         }
47218         this.owner.fireEvent('activate', this);
47219     },
47220
47221     // private
47222     adjustFont: function(btn){
47223         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47224         //if(Roo.isSafari){ // safari
47225         //    adjust *= 2;
47226        // }
47227         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47228         if(Roo.isSafari){ // safari
47229             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47230             v =  (v < 10) ? 10 : v;
47231             v =  (v > 48) ? 48 : v;
47232             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47233             
47234         }
47235         
47236         
47237         v = Math.max(1, v+adjust);
47238         
47239         this.execCmd('FontSize', v  );
47240     },
47241
47242     onEditorEvent : function(e)
47243     {
47244         this.owner.fireEvent('editorevent', this, e);
47245       //  this.updateToolbar();
47246         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47247     },
47248
47249     insertTag : function(tg)
47250     {
47251         // could be a bit smarter... -> wrap the current selected tRoo..
47252         if (tg.toLowerCase() == 'span' ||
47253             tg.toLowerCase() == 'code' ||
47254             tg.toLowerCase() == 'sup' ||
47255             tg.toLowerCase() == 'sub' 
47256             ) {
47257             
47258             range = this.createRange(this.getSelection());
47259             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47260             wrappingNode.appendChild(range.extractContents());
47261             range.insertNode(wrappingNode);
47262
47263             return;
47264             
47265             
47266             
47267         }
47268         this.execCmd("formatblock",   tg);
47269         this.undoManager.addEvent(); 
47270     },
47271     
47272     insertText : function(txt)
47273     {
47274         
47275         
47276         var range = this.createRange();
47277         range.deleteContents();
47278                //alert(Sender.getAttribute('label'));
47279                
47280         range.insertNode(this.doc.createTextNode(txt));
47281         this.undoManager.addEvent();
47282     } ,
47283     
47284      
47285
47286     /**
47287      * Executes a Midas editor command on the editor document and performs necessary focus and
47288      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47289      * @param {String} cmd The Midas command
47290      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47291      */
47292     relayCmd : function(cmd, value){
47293         this.win.focus();
47294         this.execCmd(cmd, value);
47295         this.owner.fireEvent('editorevent', this);
47296         //this.updateToolbar();
47297         this.owner.deferFocus();
47298     },
47299
47300     /**
47301      * Executes a Midas editor command directly on the editor document.
47302      * For visual commands, you should use {@link #relayCmd} instead.
47303      * <b>This should only be called after the editor is initialized.</b>
47304      * @param {String} cmd The Midas command
47305      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47306      */
47307     execCmd : function(cmd, value){
47308         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47309         this.syncValue();
47310     },
47311  
47312  
47313    
47314     /**
47315      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47316      * to insert tRoo.
47317      * @param {String} text | dom node.. 
47318      */
47319     insertAtCursor : function(text)
47320     {
47321         
47322         if(!this.activated){
47323             return;
47324         }
47325          
47326         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47327             this.win.focus();
47328             
47329             
47330             // from jquery ui (MIT licenced)
47331             var range, node;
47332             var win = this.win;
47333             
47334             if (win.getSelection && win.getSelection().getRangeAt) {
47335                 
47336                 // delete the existing?
47337                 
47338                 this.createRange(this.getSelection()).deleteContents();
47339                 range = win.getSelection().getRangeAt(0);
47340                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47341                 range.insertNode(node);
47342                 range = range.cloneRange();
47343                 range.collapse(false);
47344                  
47345                 win.getSelection().removeAllRanges();
47346                 win.getSelection().addRange(range);
47347                 
47348                 
47349                 
47350             } else if (win.document.selection && win.document.selection.createRange) {
47351                 // no firefox support
47352                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47353                 win.document.selection.createRange().pasteHTML(txt);
47354             
47355             } else {
47356                 // no firefox support
47357                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47358                 this.execCmd('InsertHTML', txt);
47359             } 
47360             this.syncValue();
47361             
47362             this.deferFocus();
47363         }
47364     },
47365  // private
47366     mozKeyPress : function(e){
47367         if(e.ctrlKey){
47368             var c = e.getCharCode(), cmd;
47369           
47370             if(c > 0){
47371                 c = String.fromCharCode(c).toLowerCase();
47372                 switch(c){
47373                     case 'b':
47374                         cmd = 'bold';
47375                         break;
47376                     case 'i':
47377                         cmd = 'italic';
47378                         break;
47379                     
47380                     case 'u':
47381                         cmd = 'underline';
47382                         break;
47383                     
47384                     //case 'v':
47385                       //  this.cleanUpPaste.defer(100, this);
47386                       //  return;
47387                         
47388                 }
47389                 if(cmd){
47390                     this.win.focus();
47391                     this.execCmd(cmd);
47392                     this.deferFocus();
47393                     e.preventDefault();
47394                 }
47395                 
47396             }
47397         }
47398     },
47399
47400     // private
47401     fixKeys : function(){ // load time branching for fastest keydown performance
47402         if(Roo.isIE){
47403             return function(e){
47404                 var k = e.getKey(), r;
47405                 if(k == e.TAB){
47406                     e.stopEvent();
47407                     r = this.doc.selection.createRange();
47408                     if(r){
47409                         r.collapse(true);
47410                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47411                         this.deferFocus();
47412                     }
47413                     return;
47414                 }
47415                 
47416                 if(k == e.ENTER){
47417                     r = this.doc.selection.createRange();
47418                     if(r){
47419                         var target = r.parentElement();
47420                         if(!target || target.tagName.toLowerCase() != 'li'){
47421                             e.stopEvent();
47422                             r.pasteHTML('<br/>');
47423                             r.collapse(false);
47424                             r.select();
47425                         }
47426                     }
47427                 }
47428                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47429                 //    this.cleanUpPaste.defer(100, this);
47430                 //    return;
47431                 //}
47432                 
47433                 
47434             };
47435         }else if(Roo.isOpera){
47436             return function(e){
47437                 var k = e.getKey();
47438                 if(k == e.TAB){
47439                     e.stopEvent();
47440                     this.win.focus();
47441                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47442                     this.deferFocus();
47443                 }
47444                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47445                 //    this.cleanUpPaste.defer(100, this);
47446                  //   return;
47447                 //}
47448                 
47449             };
47450         }else if(Roo.isSafari){
47451             return function(e){
47452                 var k = e.getKey();
47453                 
47454                 if(k == e.TAB){
47455                     e.stopEvent();
47456                     this.execCmd('InsertText','\t');
47457                     this.deferFocus();
47458                     return;
47459                 }
47460                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47461                  //   this.cleanUpPaste.defer(100, this);
47462                  //   return;
47463                // }
47464                 
47465              };
47466         }
47467     }(),
47468     
47469     getAllAncestors: function()
47470     {
47471         var p = this.getSelectedNode();
47472         var a = [];
47473         if (!p) {
47474             a.push(p); // push blank onto stack..
47475             p = this.getParentElement();
47476         }
47477         
47478         
47479         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47480             a.push(p);
47481             p = p.parentNode;
47482         }
47483         a.push(this.doc.body);
47484         return a;
47485     },
47486     lastSel : false,
47487     lastSelNode : false,
47488     
47489     
47490     getSelection : function() 
47491     {
47492         this.assignDocWin();
47493         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47494     },
47495     /**
47496      * Select a dom node
47497      * @param {DomElement} node the node to select
47498      */
47499     selectNode : function(node)
47500     {
47501         var nodeRange = node.ownerDocument.createRange();
47502         try {
47503             nodeRange.selectNode(node);
47504         } catch (e) {
47505             nodeRange.selectNodeContents(node);
47506         }
47507         //nodeRange.collapse(true);
47508         var s = this.win.getSelection();
47509         s.removeAllRanges();
47510         s.addRange(nodeRange);
47511     },
47512     
47513     getSelectedNode: function() 
47514     {
47515         // this may only work on Gecko!!!
47516         
47517         // should we cache this!!!!
47518         
47519         
47520         
47521          
47522         var range = this.createRange(this.getSelection()).cloneRange();
47523         
47524         if (Roo.isIE) {
47525             var parent = range.parentElement();
47526             while (true) {
47527                 var testRange = range.duplicate();
47528                 testRange.moveToElementText(parent);
47529                 if (testRange.inRange(range)) {
47530                     break;
47531                 }
47532                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47533                     break;
47534                 }
47535                 parent = parent.parentElement;
47536             }
47537             return parent;
47538         }
47539         
47540         // is ancestor a text element.
47541         var ac =  range.commonAncestorContainer;
47542         if (ac.nodeType == 3) {
47543             ac = ac.parentNode;
47544         }
47545         
47546         var ar = ac.childNodes;
47547          
47548         var nodes = [];
47549         var other_nodes = [];
47550         var has_other_nodes = false;
47551         for (var i=0;i<ar.length;i++) {
47552             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47553                 continue;
47554             }
47555             // fullly contained node.
47556             
47557             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47558                 nodes.push(ar[i]);
47559                 continue;
47560             }
47561             
47562             // probably selected..
47563             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47564                 other_nodes.push(ar[i]);
47565                 continue;
47566             }
47567             // outer..
47568             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47569                 continue;
47570             }
47571             
47572             
47573             has_other_nodes = true;
47574         }
47575         if (!nodes.length && other_nodes.length) {
47576             nodes= other_nodes;
47577         }
47578         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47579             return false;
47580         }
47581         
47582         return nodes[0];
47583     },
47584     createRange: function(sel)
47585     {
47586         // this has strange effects when using with 
47587         // top toolbar - not sure if it's a great idea.
47588         //this.editor.contentWindow.focus();
47589         if (typeof sel != "undefined") {
47590             try {
47591                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47592             } catch(e) {
47593                 return this.doc.createRange();
47594             }
47595         } else {
47596             return this.doc.createRange();
47597         }
47598     },
47599     getParentElement: function()
47600     {
47601         
47602         this.assignDocWin();
47603         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47604         
47605         var range = this.createRange(sel);
47606          
47607         try {
47608             var p = range.commonAncestorContainer;
47609             while (p.nodeType == 3) { // text node
47610                 p = p.parentNode;
47611             }
47612             return p;
47613         } catch (e) {
47614             return null;
47615         }
47616     
47617     },
47618     /***
47619      *
47620      * Range intersection.. the hard stuff...
47621      *  '-1' = before
47622      *  '0' = hits..
47623      *  '1' = after.
47624      *         [ -- selected range --- ]
47625      *   [fail]                        [fail]
47626      *
47627      *    basically..
47628      *      if end is before start or  hits it. fail.
47629      *      if start is after end or hits it fail.
47630      *
47631      *   if either hits (but other is outside. - then it's not 
47632      *   
47633      *    
47634      **/
47635     
47636     
47637     // @see http://www.thismuchiknow.co.uk/?p=64.
47638     rangeIntersectsNode : function(range, node)
47639     {
47640         var nodeRange = node.ownerDocument.createRange();
47641         try {
47642             nodeRange.selectNode(node);
47643         } catch (e) {
47644             nodeRange.selectNodeContents(node);
47645         }
47646     
47647         var rangeStartRange = range.cloneRange();
47648         rangeStartRange.collapse(true);
47649     
47650         var rangeEndRange = range.cloneRange();
47651         rangeEndRange.collapse(false);
47652     
47653         var nodeStartRange = nodeRange.cloneRange();
47654         nodeStartRange.collapse(true);
47655     
47656         var nodeEndRange = nodeRange.cloneRange();
47657         nodeEndRange.collapse(false);
47658     
47659         return rangeStartRange.compareBoundaryPoints(
47660                  Range.START_TO_START, nodeEndRange) == -1 &&
47661                rangeEndRange.compareBoundaryPoints(
47662                  Range.START_TO_START, nodeStartRange) == 1;
47663         
47664          
47665     },
47666     rangeCompareNode : function(range, node)
47667     {
47668         var nodeRange = node.ownerDocument.createRange();
47669         try {
47670             nodeRange.selectNode(node);
47671         } catch (e) {
47672             nodeRange.selectNodeContents(node);
47673         }
47674         
47675         
47676         range.collapse(true);
47677     
47678         nodeRange.collapse(true);
47679      
47680         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47681         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47682          
47683         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47684         
47685         var nodeIsBefore   =  ss == 1;
47686         var nodeIsAfter    = ee == -1;
47687         
47688         if (nodeIsBefore && nodeIsAfter) {
47689             return 0; // outer
47690         }
47691         if (!nodeIsBefore && nodeIsAfter) {
47692             return 1; //right trailed.
47693         }
47694         
47695         if (nodeIsBefore && !nodeIsAfter) {
47696             return 2;  // left trailed.
47697         }
47698         // fully contined.
47699         return 3;
47700     },
47701  
47702     cleanWordChars : function(input) {// change the chars to hex code
47703         
47704        var swapCodes  = [ 
47705             [    8211, "&#8211;" ], 
47706             [    8212, "&#8212;" ], 
47707             [    8216,  "'" ],  
47708             [    8217, "'" ],  
47709             [    8220, '"' ],  
47710             [    8221, '"' ],  
47711             [    8226, "*" ],  
47712             [    8230, "..." ]
47713         ]; 
47714         var output = input;
47715         Roo.each(swapCodes, function(sw) { 
47716             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47717             
47718             output = output.replace(swapper, sw[1]);
47719         });
47720         
47721         return output;
47722     },
47723     
47724      
47725     
47726         
47727     
47728     cleanUpChild : function (node)
47729     {
47730         
47731         new Roo.htmleditor.FilterComment({node : node});
47732         new Roo.htmleditor.FilterAttributes({
47733                 node : node,
47734                 attrib_black : this.ablack,
47735                 attrib_clean : this.aclean,
47736                 style_white : this.cwhite,
47737                 style_black : this.cblack
47738         });
47739         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47740         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47741          
47742         
47743     },
47744     
47745     /**
47746      * Clean up MS wordisms...
47747      * @deprecated - use filter directly
47748      */
47749     cleanWord : function(node)
47750     {
47751         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47752         
47753     },
47754    
47755     
47756     /**
47757
47758      * @deprecated - use filters
47759      */
47760     cleanTableWidths : function(node)
47761     {
47762         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47763         
47764  
47765     },
47766     
47767      
47768         
47769     applyBlacklists : function()
47770     {
47771         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47772         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47773         
47774         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47775         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47776         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47777         
47778         this.white = [];
47779         this.black = [];
47780         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47781             if (b.indexOf(tag) > -1) {
47782                 return;
47783             }
47784             this.white.push(tag);
47785             
47786         }, this);
47787         
47788         Roo.each(w, function(tag) {
47789             if (b.indexOf(tag) > -1) {
47790                 return;
47791             }
47792             if (this.white.indexOf(tag) > -1) {
47793                 return;
47794             }
47795             this.white.push(tag);
47796             
47797         }, this);
47798         
47799         
47800         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47801             if (w.indexOf(tag) > -1) {
47802                 return;
47803             }
47804             this.black.push(tag);
47805             
47806         }, this);
47807         
47808         Roo.each(b, function(tag) {
47809             if (w.indexOf(tag) > -1) {
47810                 return;
47811             }
47812             if (this.black.indexOf(tag) > -1) {
47813                 return;
47814             }
47815             this.black.push(tag);
47816             
47817         }, this);
47818         
47819         
47820         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47821         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47822         
47823         this.cwhite = [];
47824         this.cblack = [];
47825         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47826             if (b.indexOf(tag) > -1) {
47827                 return;
47828             }
47829             this.cwhite.push(tag);
47830             
47831         }, this);
47832         
47833         Roo.each(w, function(tag) {
47834             if (b.indexOf(tag) > -1) {
47835                 return;
47836             }
47837             if (this.cwhite.indexOf(tag) > -1) {
47838                 return;
47839             }
47840             this.cwhite.push(tag);
47841             
47842         }, this);
47843         
47844         
47845         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47846             if (w.indexOf(tag) > -1) {
47847                 return;
47848             }
47849             this.cblack.push(tag);
47850             
47851         }, this);
47852         
47853         Roo.each(b, function(tag) {
47854             if (w.indexOf(tag) > -1) {
47855                 return;
47856             }
47857             if (this.cblack.indexOf(tag) > -1) {
47858                 return;
47859             }
47860             this.cblack.push(tag);
47861             
47862         }, this);
47863     },
47864     
47865     setStylesheets : function(stylesheets)
47866     {
47867         if(typeof(stylesheets) == 'string'){
47868             Roo.get(this.iframe.contentDocument.head).createChild({
47869                 tag : 'link',
47870                 rel : 'stylesheet',
47871                 type : 'text/css',
47872                 href : stylesheets
47873             });
47874             
47875             return;
47876         }
47877         var _this = this;
47878      
47879         Roo.each(stylesheets, function(s) {
47880             if(!s.length){
47881                 return;
47882             }
47883             
47884             Roo.get(_this.iframe.contentDocument.head).createChild({
47885                 tag : 'link',
47886                 rel : 'stylesheet',
47887                 type : 'text/css',
47888                 href : s
47889             });
47890         });
47891
47892         
47893     },
47894     
47895     removeStylesheets : function()
47896     {
47897         var _this = this;
47898         
47899         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47900             s.remove();
47901         });
47902     },
47903     
47904     setStyle : function(style)
47905     {
47906         Roo.get(this.iframe.contentDocument.head).createChild({
47907             tag : 'style',
47908             type : 'text/css',
47909             html : style
47910         });
47911
47912         return;
47913     }
47914     
47915     // hide stuff that is not compatible
47916     /**
47917      * @event blur
47918      * @hide
47919      */
47920     /**
47921      * @event change
47922      * @hide
47923      */
47924     /**
47925      * @event focus
47926      * @hide
47927      */
47928     /**
47929      * @event specialkey
47930      * @hide
47931      */
47932     /**
47933      * @cfg {String} fieldClass @hide
47934      */
47935     /**
47936      * @cfg {String} focusClass @hide
47937      */
47938     /**
47939      * @cfg {String} autoCreate @hide
47940      */
47941     /**
47942      * @cfg {String} inputType @hide
47943      */
47944     /**
47945      * @cfg {String} invalidClass @hide
47946      */
47947     /**
47948      * @cfg {String} invalidText @hide
47949      */
47950     /**
47951      * @cfg {String} msgFx @hide
47952      */
47953     /**
47954      * @cfg {String} validateOnBlur @hide
47955      */
47956 });
47957
47958 Roo.HtmlEditorCore.white = [
47959         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47960         
47961        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47962        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47963        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47964        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47965        'TABLE',   'UL',         'XMP', 
47966        
47967        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47968       'THEAD',   'TR', 
47969      
47970       'DIR', 'MENU', 'OL', 'UL', 'DL',
47971        
47972       'EMBED',  'OBJECT'
47973 ];
47974
47975
47976 Roo.HtmlEditorCore.black = [
47977     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47978         'APPLET', // 
47979         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47980         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47981         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47982         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47983         //'FONT' // CLEAN LATER..
47984         'COLGROUP', 'COL'  // messy tables.
47985         
47986 ];
47987 Roo.HtmlEditorCore.clean = [ // ?? needed???
47988      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47989 ];
47990 Roo.HtmlEditorCore.tag_remove = [
47991     'FONT', 'TBODY'  
47992 ];
47993 // attributes..
47994
47995 Roo.HtmlEditorCore.ablack = [
47996     'on'
47997 ];
47998     
47999 Roo.HtmlEditorCore.aclean = [ 
48000     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
48001 ];
48002
48003 // protocols..
48004 Roo.HtmlEditorCore.pwhite= [
48005         'http',  'https',  'mailto'
48006 ];
48007
48008 // white listed style attributes.
48009 Roo.HtmlEditorCore.cwhite= [
48010       //  'text-align', /// default is to allow most things..
48011       
48012          
48013 //        'font-size'//??
48014 ];
48015
48016 // black listed style attributes.
48017 Roo.HtmlEditorCore.cblack= [
48018       //  'font-size' -- this can be set by the project 
48019 ];
48020
48021
48022
48023
48024     //<script type="text/javascript">
48025
48026 /*
48027  * Ext JS Library 1.1.1
48028  * Copyright(c) 2006-2007, Ext JS, LLC.
48029  * Licence LGPL
48030  * 
48031  */
48032  
48033  
48034 Roo.form.HtmlEditor = function(config){
48035     
48036     
48037     
48038     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48039     
48040     if (!this.toolbars) {
48041         this.toolbars = [];
48042     }
48043     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48044     
48045     
48046 };
48047
48048 /**
48049  * @class Roo.form.HtmlEditor
48050  * @extends Roo.form.Field
48051  * Provides a lightweight HTML Editor component.
48052  *
48053  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48054  * 
48055  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48056  * supported by this editor.</b><br/><br/>
48057  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48058  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48059  */
48060 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48061     /**
48062      * @cfg {Boolean} clearUp
48063      */
48064     clearUp : true,
48065       /**
48066      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48067      */
48068     toolbars : false,
48069    
48070      /**
48071      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48072      *                        Roo.resizable.
48073      */
48074     resizable : false,
48075      /**
48076      * @cfg {Number} height (in pixels)
48077      */   
48078     height: 300,
48079    /**
48080      * @cfg {Number} width (in pixels)
48081      */   
48082     width: 500,
48083     
48084     /**
48085      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48086      * 
48087      */
48088     stylesheets: false,
48089     
48090     
48091      /**
48092      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48093      * 
48094      */
48095     cblack: false,
48096     /**
48097      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48098      * 
48099      */
48100     cwhite: false,
48101     
48102      /**
48103      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48104      * 
48105      */
48106     black: false,
48107     /**
48108      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48109      * 
48110      */
48111     white: false,
48112     /**
48113      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48114      */
48115     allowComments: false,
48116     /**
48117      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48118      */
48119     
48120     
48121      bodyCls : '',
48122     
48123     // id of frame..
48124     frameId: false,
48125     
48126     // private properties
48127     validationEvent : false,
48128     deferHeight: true,
48129     initialized : false,
48130     activated : false,
48131     
48132     onFocus : Roo.emptyFn,
48133     iframePad:3,
48134     hideMode:'offsets',
48135     
48136     actionMode : 'container', // defaults to hiding it...
48137     
48138     defaultAutoCreate : { // modified by initCompnoent..
48139         tag: "textarea",
48140         style:"width:500px;height:300px;",
48141         autocomplete: "new-password"
48142     },
48143
48144     // private
48145     initComponent : function(){
48146         this.addEvents({
48147             /**
48148              * @event initialize
48149              * Fires when the editor is fully initialized (including the iframe)
48150              * @param {HtmlEditor} this
48151              */
48152             initialize: true,
48153             /**
48154              * @event activate
48155              * Fires when the editor is first receives the focus. Any insertion must wait
48156              * until after this event.
48157              * @param {HtmlEditor} this
48158              */
48159             activate: true,
48160              /**
48161              * @event beforesync
48162              * Fires before the textarea is updated with content from the editor iframe. Return false
48163              * to cancel the sync.
48164              * @param {HtmlEditor} this
48165              * @param {String} html
48166              */
48167             beforesync: true,
48168              /**
48169              * @event beforepush
48170              * Fires before the iframe editor is updated with content from the textarea. Return false
48171              * to cancel the push.
48172              * @param {HtmlEditor} this
48173              * @param {String} html
48174              */
48175             beforepush: true,
48176              /**
48177              * @event sync
48178              * Fires when the textarea is updated with content from the editor iframe.
48179              * @param {HtmlEditor} this
48180              * @param {String} html
48181              */
48182             sync: true,
48183              /**
48184              * @event push
48185              * Fires when the iframe editor is updated with content from the textarea.
48186              * @param {HtmlEditor} this
48187              * @param {String} html
48188              */
48189             push: true,
48190              /**
48191              * @event editmodechange
48192              * Fires when the editor switches edit modes
48193              * @param {HtmlEditor} this
48194              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48195              */
48196             editmodechange: true,
48197             /**
48198              * @event editorevent
48199              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48200              * @param {HtmlEditor} this
48201              */
48202             editorevent: true,
48203             /**
48204              * @event firstfocus
48205              * Fires when on first focus - needed by toolbars..
48206              * @param {HtmlEditor} this
48207              */
48208             firstfocus: true,
48209             /**
48210              * @event autosave
48211              * Auto save the htmlEditor value as a file into Events
48212              * @param {HtmlEditor} this
48213              */
48214             autosave: true,
48215             /**
48216              * @event savedpreview
48217              * preview the saved version of htmlEditor
48218              * @param {HtmlEditor} this
48219              */
48220             savedpreview: true,
48221             
48222             /**
48223             * @event stylesheetsclick
48224             * Fires when press the Sytlesheets button
48225             * @param {Roo.HtmlEditorCore} this
48226             */
48227             stylesheetsclick: true,
48228             /**
48229             * @event paste
48230             * Fires when press user pastes into the editor
48231             * @param {Roo.HtmlEditorCore} this
48232             */
48233             paste: true 
48234         });
48235         this.defaultAutoCreate =  {
48236             tag: "textarea",
48237             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48238             autocomplete: "new-password"
48239         };
48240     },
48241
48242     /**
48243      * Protected method that will not generally be called directly. It
48244      * is called when the editor creates its toolbar. Override this method if you need to
48245      * add custom toolbar buttons.
48246      * @param {HtmlEditor} editor
48247      */
48248     createToolbar : function(editor){
48249         Roo.log("create toolbars");
48250         if (!editor.toolbars || !editor.toolbars.length) {
48251             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48252         }
48253         
48254         for (var i =0 ; i < editor.toolbars.length;i++) {
48255             editor.toolbars[i] = Roo.factory(
48256                     typeof(editor.toolbars[i]) == 'string' ?
48257                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48258                 Roo.form.HtmlEditor);
48259             editor.toolbars[i].init(editor);
48260         }
48261          
48262         
48263     },
48264
48265      
48266     // private
48267     onRender : function(ct, position)
48268     {
48269         var _t = this;
48270         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48271         
48272         this.wrap = this.el.wrap({
48273             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48274         });
48275         
48276         this.editorcore.onRender(ct, position);
48277          
48278         if (this.resizable) {
48279             this.resizeEl = new Roo.Resizable(this.wrap, {
48280                 pinned : true,
48281                 wrap: true,
48282                 dynamic : true,
48283                 minHeight : this.height,
48284                 height: this.height,
48285                 handles : this.resizable,
48286                 width: this.width,
48287                 listeners : {
48288                     resize : function(r, w, h) {
48289                         _t.onResize(w,h); // -something
48290                     }
48291                 }
48292             });
48293             
48294         }
48295         this.createToolbar(this);
48296        
48297         
48298         if(!this.width){
48299             this.setSize(this.wrap.getSize());
48300         }
48301         if (this.resizeEl) {
48302             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48303             // should trigger onReize..
48304         }
48305         
48306         this.keyNav = new Roo.KeyNav(this.el, {
48307             
48308             "tab" : function(e){
48309                 e.preventDefault();
48310                 
48311                 var value = this.getValue();
48312                 
48313                 var start = this.el.dom.selectionStart;
48314                 var end = this.el.dom.selectionEnd;
48315                 
48316                 if(!e.shiftKey){
48317                     
48318                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48319                     this.el.dom.setSelectionRange(end + 1, end + 1);
48320                     return;
48321                 }
48322                 
48323                 var f = value.substring(0, start).split("\t");
48324                 
48325                 if(f.pop().length != 0){
48326                     return;
48327                 }
48328                 
48329                 this.setValue(f.join("\t") + value.substring(end));
48330                 this.el.dom.setSelectionRange(start - 1, start - 1);
48331                 
48332             },
48333             
48334             "home" : function(e){
48335                 e.preventDefault();
48336                 
48337                 var curr = this.el.dom.selectionStart;
48338                 var lines = this.getValue().split("\n");
48339                 
48340                 if(!lines.length){
48341                     return;
48342                 }
48343                 
48344                 if(e.ctrlKey){
48345                     this.el.dom.setSelectionRange(0, 0);
48346                     return;
48347                 }
48348                 
48349                 var pos = 0;
48350                 
48351                 for (var i = 0; i < lines.length;i++) {
48352                     pos += lines[i].length;
48353                     
48354                     if(i != 0){
48355                         pos += 1;
48356                     }
48357                     
48358                     if(pos < curr){
48359                         continue;
48360                     }
48361                     
48362                     pos -= lines[i].length;
48363                     
48364                     break;
48365                 }
48366                 
48367                 if(!e.shiftKey){
48368                     this.el.dom.setSelectionRange(pos, pos);
48369                     return;
48370                 }
48371                 
48372                 this.el.dom.selectionStart = pos;
48373                 this.el.dom.selectionEnd = curr;
48374             },
48375             
48376             "end" : function(e){
48377                 e.preventDefault();
48378                 
48379                 var curr = this.el.dom.selectionStart;
48380                 var lines = this.getValue().split("\n");
48381                 
48382                 if(!lines.length){
48383                     return;
48384                 }
48385                 
48386                 if(e.ctrlKey){
48387                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48388                     return;
48389                 }
48390                 
48391                 var pos = 0;
48392                 
48393                 for (var i = 0; i < lines.length;i++) {
48394                     
48395                     pos += lines[i].length;
48396                     
48397                     if(i != 0){
48398                         pos += 1;
48399                     }
48400                     
48401                     if(pos < curr){
48402                         continue;
48403                     }
48404                     
48405                     break;
48406                 }
48407                 
48408                 if(!e.shiftKey){
48409                     this.el.dom.setSelectionRange(pos, pos);
48410                     return;
48411                 }
48412                 
48413                 this.el.dom.selectionStart = curr;
48414                 this.el.dom.selectionEnd = pos;
48415             },
48416
48417             scope : this,
48418
48419             doRelay : function(foo, bar, hname){
48420                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48421             },
48422
48423             forceKeyDown: true
48424         });
48425         
48426 //        if(this.autosave && this.w){
48427 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48428 //        }
48429     },
48430
48431     // private
48432     onResize : function(w, h)
48433     {
48434         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48435         var ew = false;
48436         var eh = false;
48437         
48438         if(this.el ){
48439             if(typeof w == 'number'){
48440                 var aw = w - this.wrap.getFrameWidth('lr');
48441                 this.el.setWidth(this.adjustWidth('textarea', aw));
48442                 ew = aw;
48443             }
48444             if(typeof h == 'number'){
48445                 var tbh = 0;
48446                 for (var i =0; i < this.toolbars.length;i++) {
48447                     // fixme - ask toolbars for heights?
48448                     tbh += this.toolbars[i].tb.el.getHeight();
48449                     if (this.toolbars[i].footer) {
48450                         tbh += this.toolbars[i].footer.el.getHeight();
48451                     }
48452                 }
48453                 
48454                 
48455                 
48456                 
48457                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48458                 ah -= 5; // knock a few pixes off for look..
48459 //                Roo.log(ah);
48460                 this.el.setHeight(this.adjustWidth('textarea', ah));
48461                 var eh = ah;
48462             }
48463         }
48464         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48465         this.editorcore.onResize(ew,eh);
48466         
48467     },
48468
48469     /**
48470      * Toggles the editor between standard and source edit mode.
48471      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48472      */
48473     toggleSourceEdit : function(sourceEditMode)
48474     {
48475         this.editorcore.toggleSourceEdit(sourceEditMode);
48476         
48477         if(this.editorcore.sourceEditMode){
48478             Roo.log('editor - showing textarea');
48479             
48480 //            Roo.log('in');
48481 //            Roo.log(this.syncValue());
48482             this.editorcore.syncValue();
48483             this.el.removeClass('x-hidden');
48484             this.el.dom.removeAttribute('tabIndex');
48485             this.el.focus();
48486             this.el.dom.scrollTop = 0;
48487             
48488             
48489             for (var i = 0; i < this.toolbars.length; i++) {
48490                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48491                     this.toolbars[i].tb.hide();
48492                     this.toolbars[i].footer.hide();
48493                 }
48494             }
48495             
48496         }else{
48497             Roo.log('editor - hiding textarea');
48498 //            Roo.log('out')
48499 //            Roo.log(this.pushValue()); 
48500             this.editorcore.pushValue();
48501             
48502             this.el.addClass('x-hidden');
48503             this.el.dom.setAttribute('tabIndex', -1);
48504             
48505             for (var i = 0; i < this.toolbars.length; i++) {
48506                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48507                     this.toolbars[i].tb.show();
48508                     this.toolbars[i].footer.show();
48509                 }
48510             }
48511             
48512             //this.deferFocus();
48513         }
48514         
48515         this.setSize(this.wrap.getSize());
48516         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48517         
48518         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48519     },
48520  
48521     // private (for BoxComponent)
48522     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48523
48524     // private (for BoxComponent)
48525     getResizeEl : function(){
48526         return this.wrap;
48527     },
48528
48529     // private (for BoxComponent)
48530     getPositionEl : function(){
48531         return this.wrap;
48532     },
48533
48534     // private
48535     initEvents : function(){
48536         this.originalValue = this.getValue();
48537     },
48538
48539     /**
48540      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48541      * @method
48542      */
48543     markInvalid : Roo.emptyFn,
48544     /**
48545      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48546      * @method
48547      */
48548     clearInvalid : Roo.emptyFn,
48549
48550     setValue : function(v){
48551         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48552         this.editorcore.pushValue();
48553     },
48554
48555      
48556     // private
48557     deferFocus : function(){
48558         this.focus.defer(10, this);
48559     },
48560
48561     // doc'ed in Field
48562     focus : function(){
48563         this.editorcore.focus();
48564         
48565     },
48566       
48567
48568     // private
48569     onDestroy : function(){
48570         
48571         
48572         
48573         if(this.rendered){
48574             
48575             for (var i =0; i < this.toolbars.length;i++) {
48576                 // fixme - ask toolbars for heights?
48577                 this.toolbars[i].onDestroy();
48578             }
48579             
48580             this.wrap.dom.innerHTML = '';
48581             this.wrap.remove();
48582         }
48583     },
48584
48585     // private
48586     onFirstFocus : function(){
48587         //Roo.log("onFirstFocus");
48588         this.editorcore.onFirstFocus();
48589          for (var i =0; i < this.toolbars.length;i++) {
48590             this.toolbars[i].onFirstFocus();
48591         }
48592         
48593     },
48594     
48595     // private
48596     syncValue : function()
48597     {
48598         this.editorcore.syncValue();
48599     },
48600     
48601     pushValue : function()
48602     {
48603         this.editorcore.pushValue();
48604     },
48605     
48606     setStylesheets : function(stylesheets)
48607     {
48608         this.editorcore.setStylesheets(stylesheets);
48609     },
48610     
48611     removeStylesheets : function()
48612     {
48613         this.editorcore.removeStylesheets();
48614     }
48615      
48616     
48617     // hide stuff that is not compatible
48618     /**
48619      * @event blur
48620      * @hide
48621      */
48622     /**
48623      * @event change
48624      * @hide
48625      */
48626     /**
48627      * @event focus
48628      * @hide
48629      */
48630     /**
48631      * @event specialkey
48632      * @hide
48633      */
48634     /**
48635      * @cfg {String} fieldClass @hide
48636      */
48637     /**
48638      * @cfg {String} focusClass @hide
48639      */
48640     /**
48641      * @cfg {String} autoCreate @hide
48642      */
48643     /**
48644      * @cfg {String} inputType @hide
48645      */
48646     /**
48647      * @cfg {String} invalidClass @hide
48648      */
48649     /**
48650      * @cfg {String} invalidText @hide
48651      */
48652     /**
48653      * @cfg {String} msgFx @hide
48654      */
48655     /**
48656      * @cfg {String} validateOnBlur @hide
48657      */
48658 });
48659  
48660     // <script type="text/javascript">
48661 /*
48662  * Based on
48663  * Ext JS Library 1.1.1
48664  * Copyright(c) 2006-2007, Ext JS, LLC.
48665  *  
48666  
48667  */
48668
48669 /**
48670  * @class Roo.form.HtmlEditorToolbar1
48671  * Basic Toolbar
48672  * 
48673  * Usage:
48674  *
48675  new Roo.form.HtmlEditor({
48676     ....
48677     toolbars : [
48678         new Roo.form.HtmlEditorToolbar1({
48679             disable : { fonts: 1 , format: 1, ..., ... , ...],
48680             btns : [ .... ]
48681         })
48682     }
48683      
48684  * 
48685  * @cfg {Object} disable List of elements to disable..
48686  * @cfg {Array} btns List of additional buttons.
48687  * 
48688  * 
48689  * NEEDS Extra CSS? 
48690  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48691  */
48692  
48693 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48694 {
48695     
48696     Roo.apply(this, config);
48697     
48698     // default disabled, based on 'good practice'..
48699     this.disable = this.disable || {};
48700     Roo.applyIf(this.disable, {
48701         fontSize : true,
48702         colors : true,
48703         specialElements : true
48704     });
48705     
48706     
48707     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48708     // dont call parent... till later.
48709 }
48710
48711 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48712     
48713     tb: false,
48714     
48715     rendered: false,
48716     
48717     editor : false,
48718     editorcore : false,
48719     /**
48720      * @cfg {Object} disable  List of toolbar elements to disable
48721          
48722      */
48723     disable : false,
48724     
48725     
48726      /**
48727      * @cfg {String} createLinkText The default text for the create link prompt
48728      */
48729     createLinkText : 'Please enter the URL for the link:',
48730     /**
48731      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48732      */
48733     defaultLinkValue : 'http:/'+'/',
48734    
48735     
48736       /**
48737      * @cfg {Array} fontFamilies An array of available font families
48738      */
48739     fontFamilies : [
48740         'Arial',
48741         'Courier New',
48742         'Tahoma',
48743         'Times New Roman',
48744         'Verdana'
48745     ],
48746     
48747     specialChars : [
48748            "&#169;",
48749           "&#174;",     
48750           "&#8482;",    
48751           "&#163;" ,    
48752          // "&#8212;",    
48753           "&#8230;",    
48754           "&#247;" ,    
48755         //  "&#225;" ,     ?? a acute?
48756            "&#8364;"    , //Euro
48757        //   "&#8220;"    ,
48758         //  "&#8221;"    ,
48759         //  "&#8226;"    ,
48760           "&#176;"  //   , // degrees
48761
48762          // "&#233;"     , // e ecute
48763          // "&#250;"     , // u ecute?
48764     ],
48765     
48766     specialElements : [
48767         {
48768             text: "Insert Table",
48769             xtype: 'MenuItem',
48770             xns : Roo.Menu,
48771             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48772                 
48773         },
48774         {    
48775             text: "Insert Image",
48776             xtype: 'MenuItem',
48777             xns : Roo.Menu,
48778             ihtml : '<img src="about:blank"/>'
48779             
48780         }
48781         
48782          
48783     ],
48784     
48785     
48786     inputElements : [ 
48787             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48788             "input:submit", "input:button", "select", "textarea", "label" ],
48789     formats : [
48790         ["p"] ,  
48791         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48792         ["pre"],[ "code"], 
48793         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48794         ['div'],['span'],
48795         ['sup'],['sub']
48796     ],
48797     
48798     cleanStyles : [
48799         "font-size"
48800     ],
48801      /**
48802      * @cfg {String} defaultFont default font to use.
48803      */
48804     defaultFont: 'tahoma',
48805    
48806     fontSelect : false,
48807     
48808     
48809     formatCombo : false,
48810     
48811     init : function(editor)
48812     {
48813         this.editor = editor;
48814         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48815         var editorcore = this.editorcore;
48816         
48817         var _t = this;
48818         
48819         var fid = editorcore.frameId;
48820         var etb = this;
48821         function btn(id, toggle, handler){
48822             var xid = fid + '-'+ id ;
48823             return {
48824                 id : xid,
48825                 cmd : id,
48826                 cls : 'x-btn-icon x-edit-'+id,
48827                 enableToggle:toggle !== false,
48828                 scope: _t, // was editor...
48829                 handler:handler||_t.relayBtnCmd,
48830                 clickEvent:'mousedown',
48831                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48832                 tabIndex:-1
48833             };
48834         }
48835         
48836         
48837         
48838         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48839         this.tb = tb;
48840          // stop form submits
48841         tb.el.on('click', function(e){
48842             e.preventDefault(); // what does this do?
48843         });
48844
48845         if(!this.disable.font) { // && !Roo.isSafari){
48846             /* why no safari for fonts 
48847             editor.fontSelect = tb.el.createChild({
48848                 tag:'select',
48849                 tabIndex: -1,
48850                 cls:'x-font-select',
48851                 html: this.createFontOptions()
48852             });
48853             
48854             editor.fontSelect.on('change', function(){
48855                 var font = editor.fontSelect.dom.value;
48856                 editor.relayCmd('fontname', font);
48857                 editor.deferFocus();
48858             }, editor);
48859             
48860             tb.add(
48861                 editor.fontSelect.dom,
48862                 '-'
48863             );
48864             */
48865             
48866         };
48867         if(!this.disable.formats){
48868             this.formatCombo = new Roo.form.ComboBox({
48869                 store: new Roo.data.SimpleStore({
48870                     id : 'tag',
48871                     fields: ['tag'],
48872                     data : this.formats // from states.js
48873                 }),
48874                 blockFocus : true,
48875                 name : '',
48876                 //autoCreate : {tag: "div",  size: "20"},
48877                 displayField:'tag',
48878                 typeAhead: false,
48879                 mode: 'local',
48880                 editable : false,
48881                 triggerAction: 'all',
48882                 emptyText:'Add tag',
48883                 selectOnFocus:true,
48884                 width:135,
48885                 listeners : {
48886                     'select': function(c, r, i) {
48887                         editorcore.insertTag(r.get('tag'));
48888                         editor.focus();
48889                     }
48890                 }
48891
48892             });
48893             tb.addField(this.formatCombo);
48894             
48895         }
48896         
48897         if(!this.disable.format){
48898             tb.add(
48899                 btn('bold'),
48900                 btn('italic'),
48901                 btn('underline'),
48902                 btn('strikethrough')
48903             );
48904         };
48905         if(!this.disable.fontSize){
48906             tb.add(
48907                 '-',
48908                 
48909                 
48910                 btn('increasefontsize', false, editorcore.adjustFont),
48911                 btn('decreasefontsize', false, editorcore.adjustFont)
48912             );
48913         };
48914         
48915         
48916         if(!this.disable.colors){
48917             tb.add(
48918                 '-', {
48919                     id:editorcore.frameId +'-forecolor',
48920                     cls:'x-btn-icon x-edit-forecolor',
48921                     clickEvent:'mousedown',
48922                     tooltip: this.buttonTips['forecolor'] || undefined,
48923                     tabIndex:-1,
48924                     menu : new Roo.menu.ColorMenu({
48925                         allowReselect: true,
48926                         focus: Roo.emptyFn,
48927                         value:'000000',
48928                         plain:true,
48929                         selectHandler: function(cp, color){
48930                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48931                             editor.deferFocus();
48932                         },
48933                         scope: editorcore,
48934                         clickEvent:'mousedown'
48935                     })
48936                 }, {
48937                     id:editorcore.frameId +'backcolor',
48938                     cls:'x-btn-icon x-edit-backcolor',
48939                     clickEvent:'mousedown',
48940                     tooltip: this.buttonTips['backcolor'] || undefined,
48941                     tabIndex:-1,
48942                     menu : new Roo.menu.ColorMenu({
48943                         focus: Roo.emptyFn,
48944                         value:'FFFFFF',
48945                         plain:true,
48946                         allowReselect: true,
48947                         selectHandler: function(cp, color){
48948                             if(Roo.isGecko){
48949                                 editorcore.execCmd('useCSS', false);
48950                                 editorcore.execCmd('hilitecolor', color);
48951                                 editorcore.execCmd('useCSS', true);
48952                                 editor.deferFocus();
48953                             }else{
48954                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48955                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48956                                 editor.deferFocus();
48957                             }
48958                         },
48959                         scope:editorcore,
48960                         clickEvent:'mousedown'
48961                     })
48962                 }
48963             );
48964         };
48965         // now add all the items...
48966         
48967
48968         if(!this.disable.alignments){
48969             tb.add(
48970                 '-',
48971                 btn('justifyleft'),
48972                 btn('justifycenter'),
48973                 btn('justifyright')
48974             );
48975         };
48976
48977         //if(!Roo.isSafari){
48978             if(!this.disable.links){
48979                 tb.add(
48980                     '-',
48981                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48982                 );
48983             };
48984
48985             if(!this.disable.lists){
48986                 tb.add(
48987                     '-',
48988                     btn('insertorderedlist'),
48989                     btn('insertunorderedlist')
48990                 );
48991             }
48992             if(!this.disable.sourceEdit){
48993                 tb.add(
48994                     '-',
48995                     btn('sourceedit', true, function(btn){
48996                         this.toggleSourceEdit(btn.pressed);
48997                     })
48998                 );
48999             }
49000         //}
49001         
49002         var smenu = { };
49003         // special menu.. - needs to be tidied up..
49004         if (!this.disable.special) {
49005             smenu = {
49006                 text: "&#169;",
49007                 cls: 'x-edit-none',
49008                 
49009                 menu : {
49010                     items : []
49011                 }
49012             };
49013             for (var i =0; i < this.specialChars.length; i++) {
49014                 smenu.menu.items.push({
49015                     
49016                     html: this.specialChars[i],
49017                     handler: function(a,b) {
49018                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
49019                         //editor.insertAtCursor(a.html);
49020                         
49021                     },
49022                     tabIndex:-1
49023                 });
49024             }
49025             
49026             
49027             tb.add(smenu);
49028             
49029             
49030         }
49031         
49032         var cmenu = { };
49033         if (!this.disable.cleanStyles) {
49034             cmenu = {
49035                 cls: 'x-btn-icon x-btn-clear',
49036                 
49037                 menu : {
49038                     items : []
49039                 }
49040             };
49041             for (var i =0; i < this.cleanStyles.length; i++) {
49042                 cmenu.menu.items.push({
49043                     actiontype : this.cleanStyles[i],
49044                     html: 'Remove ' + this.cleanStyles[i],
49045                     handler: function(a,b) {
49046 //                        Roo.log(a);
49047 //                        Roo.log(b);
49048                         var c = Roo.get(editorcore.doc.body);
49049                         c.select('[style]').each(function(s) {
49050                             s.dom.style.removeProperty(a.actiontype);
49051                         });
49052                         editorcore.syncValue();
49053                     },
49054                     tabIndex:-1
49055                 });
49056             }
49057             cmenu.menu.items.push({
49058                 actiontype : 'tablewidths',
49059                 html: 'Remove Table Widths',
49060                 handler: function(a,b) {
49061                     editorcore.cleanTableWidths();
49062                     editorcore.syncValue();
49063                 },
49064                 tabIndex:-1
49065             });
49066             cmenu.menu.items.push({
49067                 actiontype : 'word',
49068                 html: 'Remove MS Word Formating',
49069                 handler: function(a,b) {
49070                     editorcore.cleanWord();
49071                     editorcore.syncValue();
49072                 },
49073                 tabIndex:-1
49074             });
49075             
49076             cmenu.menu.items.push({
49077                 actiontype : 'all',
49078                 html: 'Remove All Styles',
49079                 handler: function(a,b) {
49080                     
49081                     var c = Roo.get(editorcore.doc.body);
49082                     c.select('[style]').each(function(s) {
49083                         s.dom.removeAttribute('style');
49084                     });
49085                     editorcore.syncValue();
49086                 },
49087                 tabIndex:-1
49088             });
49089             
49090             cmenu.menu.items.push({
49091                 actiontype : 'all',
49092                 html: 'Remove All CSS Classes',
49093                 handler: function(a,b) {
49094                     
49095                     var c = Roo.get(editorcore.doc.body);
49096                     c.select('[class]').each(function(s) {
49097                         s.dom.removeAttribute('class');
49098                     });
49099                     editorcore.cleanWord();
49100                     editorcore.syncValue();
49101                 },
49102                 tabIndex:-1
49103             });
49104             
49105              cmenu.menu.items.push({
49106                 actiontype : 'tidy',
49107                 html: 'Tidy HTML Source',
49108                 handler: function(a,b) {
49109                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49110                     editorcore.syncValue();
49111                 },
49112                 tabIndex:-1
49113             });
49114             
49115             
49116             tb.add(cmenu);
49117         }
49118          
49119         if (!this.disable.specialElements) {
49120             var semenu = {
49121                 text: "Other;",
49122                 cls: 'x-edit-none',
49123                 menu : {
49124                     items : []
49125                 }
49126             };
49127             for (var i =0; i < this.specialElements.length; i++) {
49128                 semenu.menu.items.push(
49129                     Roo.apply({ 
49130                         handler: function(a,b) {
49131                             editor.insertAtCursor(this.ihtml);
49132                         }
49133                     }, this.specialElements[i])
49134                 );
49135                     
49136             }
49137             
49138             tb.add(semenu);
49139             
49140             
49141         }
49142          
49143         
49144         if (this.btns) {
49145             for(var i =0; i< this.btns.length;i++) {
49146                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49147                 b.cls =  'x-edit-none';
49148                 
49149                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49150                     b.cls += ' x-init-enable';
49151                 }
49152                 
49153                 b.scope = editorcore;
49154                 tb.add(b);
49155             }
49156         
49157         }
49158         
49159         
49160         
49161         // disable everything...
49162         
49163         this.tb.items.each(function(item){
49164             
49165            if(
49166                 item.id != editorcore.frameId+ '-sourceedit' && 
49167                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49168             ){
49169                 
49170                 item.disable();
49171             }
49172         });
49173         this.rendered = true;
49174         
49175         // the all the btns;
49176         editor.on('editorevent', this.updateToolbar, this);
49177         // other toolbars need to implement this..
49178         //editor.on('editmodechange', this.updateToolbar, this);
49179     },
49180     
49181     
49182     relayBtnCmd : function(btn) {
49183         this.editorcore.relayCmd(btn.cmd);
49184     },
49185     // private used internally
49186     createLink : function(){
49187         Roo.log("create link?");
49188         var url = prompt(this.createLinkText, this.defaultLinkValue);
49189         if(url && url != 'http:/'+'/'){
49190             this.editorcore.relayCmd('createlink', url);
49191         }
49192     },
49193
49194     
49195     /**
49196      * Protected method that will not generally be called directly. It triggers
49197      * a toolbar update by reading the markup state of the current selection in the editor.
49198      */
49199     updateToolbar: function(){
49200
49201         if(!this.editorcore.activated){
49202             this.editor.onFirstFocus();
49203             return;
49204         }
49205
49206         var btns = this.tb.items.map, 
49207             doc = this.editorcore.doc,
49208             frameId = this.editorcore.frameId;
49209
49210         if(!this.disable.font && !Roo.isSafari){
49211             /*
49212             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49213             if(name != this.fontSelect.dom.value){
49214                 this.fontSelect.dom.value = name;
49215             }
49216             */
49217         }
49218         if(!this.disable.format){
49219             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49220             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49221             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49222             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49223         }
49224         if(!this.disable.alignments){
49225             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49226             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49227             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49228         }
49229         if(!Roo.isSafari && !this.disable.lists){
49230             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49231             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49232         }
49233         
49234         var ans = this.editorcore.getAllAncestors();
49235         if (this.formatCombo) {
49236             
49237             
49238             var store = this.formatCombo.store;
49239             this.formatCombo.setValue("");
49240             for (var i =0; i < ans.length;i++) {
49241                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49242                     // select it..
49243                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49244                     break;
49245                 }
49246             }
49247         }
49248         
49249         
49250         
49251         // hides menus... - so this cant be on a menu...
49252         Roo.menu.MenuMgr.hideAll();
49253
49254         //this.editorsyncValue();
49255     },
49256    
49257     
49258     createFontOptions : function(){
49259         var buf = [], fs = this.fontFamilies, ff, lc;
49260         
49261         
49262         
49263         for(var i = 0, len = fs.length; i< len; i++){
49264             ff = fs[i];
49265             lc = ff.toLowerCase();
49266             buf.push(
49267                 '<option value="',lc,'" style="font-family:',ff,';"',
49268                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49269                     ff,
49270                 '</option>'
49271             );
49272         }
49273         return buf.join('');
49274     },
49275     
49276     toggleSourceEdit : function(sourceEditMode){
49277         
49278         Roo.log("toolbar toogle");
49279         if(sourceEditMode === undefined){
49280             sourceEditMode = !this.sourceEditMode;
49281         }
49282         this.sourceEditMode = sourceEditMode === true;
49283         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49284         // just toggle the button?
49285         if(btn.pressed !== this.sourceEditMode){
49286             btn.toggle(this.sourceEditMode);
49287             return;
49288         }
49289         
49290         if(sourceEditMode){
49291             Roo.log("disabling buttons");
49292             this.tb.items.each(function(item){
49293                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49294                     item.disable();
49295                 }
49296             });
49297           
49298         }else{
49299             Roo.log("enabling buttons");
49300             if(this.editorcore.initialized){
49301                 this.tb.items.each(function(item){
49302                     item.enable();
49303                 });
49304             }
49305             
49306         }
49307         Roo.log("calling toggole on editor");
49308         // tell the editor that it's been pressed..
49309         this.editor.toggleSourceEdit(sourceEditMode);
49310        
49311     },
49312      /**
49313      * Object collection of toolbar tooltips for the buttons in the editor. The key
49314      * is the command id associated with that button and the value is a valid QuickTips object.
49315      * For example:
49316 <pre><code>
49317 {
49318     bold : {
49319         title: 'Bold (Ctrl+B)',
49320         text: 'Make the selected text bold.',
49321         cls: 'x-html-editor-tip'
49322     },
49323     italic : {
49324         title: 'Italic (Ctrl+I)',
49325         text: 'Make the selected text italic.',
49326         cls: 'x-html-editor-tip'
49327     },
49328     ...
49329 </code></pre>
49330     * @type Object
49331      */
49332     buttonTips : {
49333         bold : {
49334             title: 'Bold (Ctrl+B)',
49335             text: 'Make the selected text bold.',
49336             cls: 'x-html-editor-tip'
49337         },
49338         italic : {
49339             title: 'Italic (Ctrl+I)',
49340             text: 'Make the selected text italic.',
49341             cls: 'x-html-editor-tip'
49342         },
49343         underline : {
49344             title: 'Underline (Ctrl+U)',
49345             text: 'Underline the selected text.',
49346             cls: 'x-html-editor-tip'
49347         },
49348         strikethrough : {
49349             title: 'Strikethrough',
49350             text: 'Strikethrough the selected text.',
49351             cls: 'x-html-editor-tip'
49352         },
49353         increasefontsize : {
49354             title: 'Grow Text',
49355             text: 'Increase the font size.',
49356             cls: 'x-html-editor-tip'
49357         },
49358         decreasefontsize : {
49359             title: 'Shrink Text',
49360             text: 'Decrease the font size.',
49361             cls: 'x-html-editor-tip'
49362         },
49363         backcolor : {
49364             title: 'Text Highlight Color',
49365             text: 'Change the background color of the selected text.',
49366             cls: 'x-html-editor-tip'
49367         },
49368         forecolor : {
49369             title: 'Font Color',
49370             text: 'Change the color of the selected text.',
49371             cls: 'x-html-editor-tip'
49372         },
49373         justifyleft : {
49374             title: 'Align Text Left',
49375             text: 'Align text to the left.',
49376             cls: 'x-html-editor-tip'
49377         },
49378         justifycenter : {
49379             title: 'Center Text',
49380             text: 'Center text in the editor.',
49381             cls: 'x-html-editor-tip'
49382         },
49383         justifyright : {
49384             title: 'Align Text Right',
49385             text: 'Align text to the right.',
49386             cls: 'x-html-editor-tip'
49387         },
49388         insertunorderedlist : {
49389             title: 'Bullet List',
49390             text: 'Start a bulleted list.',
49391             cls: 'x-html-editor-tip'
49392         },
49393         insertorderedlist : {
49394             title: 'Numbered List',
49395             text: 'Start a numbered list.',
49396             cls: 'x-html-editor-tip'
49397         },
49398         createlink : {
49399             title: 'Hyperlink',
49400             text: 'Make the selected text a hyperlink.',
49401             cls: 'x-html-editor-tip'
49402         },
49403         sourceedit : {
49404             title: 'Source Edit',
49405             text: 'Switch to source editing mode.',
49406             cls: 'x-html-editor-tip'
49407         }
49408     },
49409     // private
49410     onDestroy : function(){
49411         if(this.rendered){
49412             
49413             this.tb.items.each(function(item){
49414                 if(item.menu){
49415                     item.menu.removeAll();
49416                     if(item.menu.el){
49417                         item.menu.el.destroy();
49418                     }
49419                 }
49420                 item.destroy();
49421             });
49422              
49423         }
49424     },
49425     onFirstFocus: function() {
49426         this.tb.items.each(function(item){
49427            item.enable();
49428         });
49429     }
49430 });
49431
49432
49433
49434
49435 // <script type="text/javascript">
49436 /*
49437  * Based on
49438  * Ext JS Library 1.1.1
49439  * Copyright(c) 2006-2007, Ext JS, LLC.
49440  *  
49441  
49442  */
49443
49444  
49445 /**
49446  * @class Roo.form.HtmlEditor.ToolbarContext
49447  * Context Toolbar
49448  * 
49449  * Usage:
49450  *
49451  new Roo.form.HtmlEditor({
49452     ....
49453     toolbars : [
49454         { xtype: 'ToolbarStandard', styles : {} }
49455         { xtype: 'ToolbarContext', disable : {} }
49456     ]
49457 })
49458
49459      
49460  * 
49461  * @config : {Object} disable List of elements to disable.. (not done yet.)
49462  * @config : {Object} styles  Map of styles available.
49463  * 
49464  */
49465
49466 Roo.form.HtmlEditor.ToolbarContext = function(config)
49467 {
49468     
49469     Roo.apply(this, config);
49470     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49471     // dont call parent... till later.
49472     this.styles = this.styles || {};
49473 }
49474
49475  
49476
49477 Roo.form.HtmlEditor.ToolbarContext.types = {
49478     'IMG' : [
49479         {
49480             name : 'width',
49481             title: "Width",
49482             width: 40
49483         },
49484         {
49485             name : 'height',
49486             title: "Height",
49487             width: 40
49488         },
49489         {
49490             name : 'align',
49491             title: "Align",
49492             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49493             width : 80
49494             
49495         },
49496         {
49497             name : 'border',
49498             title: "Border",
49499             width: 40
49500         },
49501         {
49502             name : 'alt',
49503             title: "Alt",
49504             width: 120
49505         },
49506         {
49507             name : 'src',
49508             title: "Src",
49509             width: 220
49510         }
49511         
49512     ],
49513     
49514     'FIGURE' : [
49515         {
49516             name : 'align',
49517             title: "Align",
49518             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49519             width : 80  
49520         }
49521     ],
49522     'A' : [
49523         {
49524             name : 'name',
49525             title: "Name",
49526             width: 50
49527         },
49528         {
49529             name : 'target',
49530             title: "Target",
49531             width: 120
49532         },
49533         {
49534             name : 'href',
49535             title: "Href",
49536             width: 220
49537         } // border?
49538         
49539     ],
49540     
49541     'INPUT' : [
49542         {
49543             name : 'name',
49544             title: "name",
49545             width: 120
49546         },
49547         {
49548             name : 'value',
49549             title: "Value",
49550             width: 120
49551         },
49552         {
49553             name : 'width',
49554             title: "Width",
49555             width: 40
49556         }
49557     ],
49558     'LABEL' : [
49559          {
49560             name : 'for',
49561             title: "For",
49562             width: 120
49563         }
49564     ],
49565     'TEXTAREA' : [
49566         {
49567             name : 'name',
49568             title: "name",
49569             width: 120
49570         },
49571         {
49572             name : 'rows',
49573             title: "Rows",
49574             width: 20
49575         },
49576         {
49577             name : 'cols',
49578             title: "Cols",
49579             width: 20
49580         }
49581     ],
49582     'SELECT' : [
49583         {
49584             name : 'name',
49585             title: "name",
49586             width: 120
49587         },
49588         {
49589             name : 'selectoptions',
49590             title: "Options",
49591             width: 200
49592         }
49593     ],
49594     
49595     // should we really allow this??
49596     // should this just be 
49597     'BODY' : [
49598         
49599         {
49600             name : 'title',
49601             title: "Title",
49602             width: 200,
49603             disabled : true
49604         }
49605     ],
49606  
49607     '*' : [
49608         // empty.
49609     ]
49610
49611 };
49612
49613 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49614 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49615
49616 Roo.form.HtmlEditor.ToolbarContext.options = {
49617         'font-family'  : [ 
49618                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49619                 [ 'Courier New', 'Courier New'],
49620                 [ 'Tahoma', 'Tahoma'],
49621                 [ 'Times New Roman,serif', 'Times'],
49622                 [ 'Verdana','Verdana' ]
49623         ]
49624 };
49625
49626 // fixme - these need to be configurable..
49627  
49628
49629 //Roo.form.HtmlEditor.ToolbarContext.types
49630
49631
49632 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49633     
49634     tb: false,
49635     
49636     rendered: false,
49637     
49638     editor : false,
49639     editorcore : false,
49640     /**
49641      * @cfg {Object} disable  List of toolbar elements to disable
49642          
49643      */
49644     disable : false,
49645     /**
49646      * @cfg {Object} styles List of styles 
49647      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49648      *
49649      * These must be defined in the page, so they get rendered correctly..
49650      * .headline { }
49651      * TD.underline { }
49652      * 
49653      */
49654     styles : false,
49655     
49656     options: false,
49657     
49658     toolbars : false,
49659     
49660     init : function(editor)
49661     {
49662         this.editor = editor;
49663         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49664         var editorcore = this.editorcore;
49665         
49666         var fid = editorcore.frameId;
49667         var etb = this;
49668         function btn(id, toggle, handler){
49669             var xid = fid + '-'+ id ;
49670             return {
49671                 id : xid,
49672                 cmd : id,
49673                 cls : 'x-btn-icon x-edit-'+id,
49674                 enableToggle:toggle !== false,
49675                 scope: editorcore, // was editor...
49676                 handler:handler||editorcore.relayBtnCmd,
49677                 clickEvent:'mousedown',
49678                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49679                 tabIndex:-1
49680             };
49681         }
49682         // create a new element.
49683         var wdiv = editor.wrap.createChild({
49684                 tag: 'div'
49685             }, editor.wrap.dom.firstChild.nextSibling, true);
49686         
49687         // can we do this more than once??
49688         
49689          // stop form submits
49690       
49691  
49692         // disable everything...
49693         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49694         this.toolbars = {};
49695            
49696         for (var i in  ty) {
49697           
49698             this.toolbars[i] = this.buildToolbar(ty[i],i);
49699         }
49700         this.tb = this.toolbars.BODY;
49701         this.tb.el.show();
49702         this.buildFooter();
49703         this.footer.show();
49704         editor.on('hide', function( ) { this.footer.hide() }, this);
49705         editor.on('show', function( ) { this.footer.show() }, this);
49706         
49707          
49708         this.rendered = true;
49709         
49710         // the all the btns;
49711         editor.on('editorevent', this.updateToolbar, this);
49712         // other toolbars need to implement this..
49713         //editor.on('editmodechange', this.updateToolbar, this);
49714     },
49715     
49716     
49717     
49718     /**
49719      * Protected method that will not generally be called directly. It triggers
49720      * a toolbar update by reading the markup state of the current selection in the editor.
49721      *
49722      * Note you can force an update by calling on('editorevent', scope, false)
49723      */
49724     updateToolbar: function(editor ,ev, sel)
49725     {
49726         
49727         if (ev) {
49728             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49729         }
49730         
49731         //Roo.log(ev);
49732         // capture mouse up - this is handy for selecting images..
49733         // perhaps should go somewhere else...
49734         if(!this.editorcore.activated){
49735              this.editor.onFirstFocus();
49736             return;
49737         }
49738         //Roo.log(ev ? ev.target : 'NOTARGET');
49739         
49740         
49741         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49742         // selectNode - might want to handle IE?
49743         
49744         
49745         
49746         if (ev &&
49747             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49748             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49749             // they have click on an image...
49750             // let's see if we can change the selection...
49751             sel = ev.target;
49752             
49753             // this triggers looping?
49754             //this.editorcore.selectNode(sel);
49755              
49756         }  
49757         Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
49758         //Roo.get(node).addClass('roo-ed-selection');
49759       
49760         //var updateFooter = sel ? false : true; 
49761         
49762         
49763         var ans = this.editorcore.getAllAncestors();
49764         
49765         // pick
49766         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49767         
49768         if (!sel) { 
49769             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49770             sel = sel ? sel : this.editorcore.doc.body;
49771             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49772             
49773         }
49774         
49775         var tn = sel.tagName.toUpperCase();
49776         var lastSel = this.tb.selectedNode;
49777         this.tb.selectedNode = sel;
49778         var left_label = tn;
49779         
49780         // ok see if we are editing a block?
49781         
49782         var db = false;
49783         // you are not actually selecting the block.
49784         if (sel && sel.hasAttribute('data-block')) {
49785             db = sel;
49786         } else if (sel && !sel.hasAttribute('contenteditable')) {
49787             var sel_el = Roo.get(sel);
49788             db = sel_el.findParent('[data-block]');
49789             var cepar = sel_el.findParent('[contenteditable=true]');
49790             if (db && cepar && cepar.tagName != 'BODY') {
49791                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49792             }   
49793         }
49794         
49795         
49796         var block = false;
49797         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49798         if (db) {
49799             block = Roo.htmleditor.Block.factory(db);
49800             
49801             
49802             if (block) {
49803                 db.className +=  ' roo-ed-selection'; // since we removed it earlier... its not there..
49804                 tn = 'BLOCK.' + db.getAttribute('data-block');
49805                 
49806                 //this.editorcore.selectNode(db);
49807                 if (typeof(this.toolbars[tn]) == 'undefined') {
49808                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49809                 }
49810                 this.toolbars[tn].selectedNode = db;
49811                 left_label = block.friendly_name;
49812                 ans = this.editorcore.getAllAncestors();
49813             }
49814             
49815                 
49816             
49817         }
49818         
49819         
49820         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49821             return; // no change?
49822         }
49823         
49824         
49825           
49826         this.tb.el.hide();
49827         ///console.log("show: " + tn);
49828         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49829         
49830         this.tb.el.show();
49831         // update name
49832         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49833         
49834         
49835         // update attributes
49836         if (block && this.tb.fields) {
49837              
49838             this.tb.fields.each(function(e) {
49839                 e.setValue(block[e.name]);
49840             });
49841             
49842             
49843         } else  if (this.tb.fields && this.tb.selectedNode) {
49844             this.tb.fields.each( function(e) {
49845                 if (e.stylename) {
49846                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49847                     return;
49848                 } 
49849                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49850             }, this);
49851             this.updateToolbarStyles(this.tb.selectedNode);  
49852         }
49853         
49854         
49855        
49856         Roo.menu.MenuMgr.hideAll();
49857
49858         
49859         
49860     
49861         // update the footer
49862         //
49863         this.updateFooter(ans);
49864              
49865     },
49866     
49867     updateToolbarStyles : function(sel)
49868     {
49869         var hasStyles = false;
49870         for(var i in this.styles) {
49871             hasStyles = true;
49872             break;
49873         }
49874         
49875         // update styles
49876         if (hasStyles && this.tb.hasStyles) { 
49877             var st = this.tb.fields.item(0);
49878             
49879             st.store.removeAll();
49880             var cn = sel.className.split(/\s+/);
49881             
49882             var avs = [];
49883             if (this.styles['*']) {
49884                 
49885                 Roo.each(this.styles['*'], function(v) {
49886                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49887                 });
49888             }
49889             if (this.styles[tn]) { 
49890                 Roo.each(this.styles[tn], function(v) {
49891                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49892                 });
49893             }
49894             
49895             st.store.loadData(avs);
49896             st.collapse();
49897             st.setValue(cn);
49898         }
49899     },
49900     
49901      
49902     updateFooter : function(ans)
49903     {
49904         var html = '';
49905         if (ans === false) {
49906             this.footDisp.dom.innerHTML = '';
49907             return;
49908         }
49909         
49910         this.footerEls = ans.reverse();
49911         Roo.each(this.footerEls, function(a,i) {
49912             if (!a) { return; }
49913             html += html.length ? ' &gt; '  :  '';
49914             
49915             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49916             
49917         });
49918        
49919         // 
49920         var sz = this.footDisp.up('td').getSize();
49921         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49922         this.footDisp.dom.style.marginLeft = '5px';
49923         
49924         this.footDisp.dom.style.overflow = 'hidden';
49925         
49926         this.footDisp.dom.innerHTML = html;
49927             
49928         
49929     },
49930    
49931        
49932     // private
49933     onDestroy : function(){
49934         if(this.rendered){
49935             
49936             this.tb.items.each(function(item){
49937                 if(item.menu){
49938                     item.menu.removeAll();
49939                     if(item.menu.el){
49940                         item.menu.el.destroy();
49941                     }
49942                 }
49943                 item.destroy();
49944             });
49945              
49946         }
49947     },
49948     onFirstFocus: function() {
49949         // need to do this for all the toolbars..
49950         this.tb.items.each(function(item){
49951            item.enable();
49952         });
49953     },
49954     buildToolbar: function(tlist, nm, friendly_name, block)
49955     {
49956         var editor = this.editor;
49957         var editorcore = this.editorcore;
49958          // create a new element.
49959         var wdiv = editor.wrap.createChild({
49960                 tag: 'div'
49961             }, editor.wrap.dom.firstChild.nextSibling, true);
49962         
49963        
49964         var tb = new Roo.Toolbar(wdiv);
49965         ///this.tb = tb; // << this sets the active toolbar..
49966         if (tlist === false && block) {
49967             tlist = block.contextMenu(this);
49968         }
49969         
49970         tb.hasStyles = false;
49971         tb.name = nm;
49972         
49973         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49974         
49975         var styles = Array.from(this.styles);
49976         
49977         
49978         // styles...
49979         if (styles && styles.length) {
49980             tb.hasStyles = true;
49981             // this needs a multi-select checkbox...
49982             tb.addField( new Roo.form.ComboBox({
49983                 store: new Roo.data.SimpleStore({
49984                     id : 'val',
49985                     fields: ['val', 'selected'],
49986                     data : [] 
49987                 }),
49988                 name : '-roo-edit-className',
49989                 attrname : 'className',
49990                 displayField: 'val',
49991                 typeAhead: false,
49992                 mode: 'local',
49993                 editable : false,
49994                 triggerAction: 'all',
49995                 emptyText:'Select Style',
49996                 selectOnFocus:true,
49997                 width: 130,
49998                 listeners : {
49999                     'select': function(c, r, i) {
50000                         // initial support only for on class per el..
50001                         tb.selectedNode.className =  r ? r.get('val') : '';
50002                         editorcore.syncValue();
50003                     }
50004                 }
50005     
50006             }));
50007         }
50008         
50009         var tbc = Roo.form.HtmlEditor.ToolbarContext;
50010         
50011         
50012         for (var i = 0; i < tlist.length; i++) {
50013             
50014             // newer versions will use xtype cfg to create menus.
50015             if (typeof(tlist[i].xtype) != 'undefined') {
50016                 
50017                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
50018                 
50019                 
50020                 continue;
50021             }
50022             
50023             var item = tlist[i];
50024             tb.add(item.title + ":&nbsp;");
50025             
50026             
50027             //optname == used so you can configure the options available..
50028             var opts = item.opts ? item.opts : false;
50029             if (item.optname) { // use the b
50030                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50031            
50032             }
50033             
50034             if (opts) {
50035                 // opts == pulldown..
50036                 tb.addField( new Roo.form.ComboBox({
50037                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50038                         id : 'val',
50039                         fields: ['val', 'display'],
50040                         data : opts  
50041                     }),
50042                     name : '-roo-edit-' + tlist[i].name,
50043                     
50044                     attrname : tlist[i].name,
50045                     stylename : item.style ? item.style : false,
50046                     
50047                     displayField: item.displayField ? item.displayField : 'val',
50048                     valueField :  'val',
50049                     typeAhead: false,
50050                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50051                     editable : false,
50052                     triggerAction: 'all',
50053                     emptyText:'Select',
50054                     selectOnFocus:true,
50055                     width: item.width ? item.width  : 130,
50056                     listeners : {
50057                         'select': function(c, r, i) {
50058                             if (tb.selectedNode.hasAttribute('data-block')) {
50059                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50060                                 b[c.attrname] = r.get('val');
50061                                 b.updateElement(tb.selectedNode);
50062                                 editorcore.syncValue();
50063                                 return;
50064                             }
50065                             
50066                             if (c.stylename) {
50067                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50068                                 editorcore.syncValue();
50069                                 return;
50070                             }
50071                             if (r === false) {
50072                                 tb.selectedNode.removeAttribute(c.attrname);
50073                                 editorcore.syncValue();
50074                                 return;
50075                             }
50076                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50077                             editorcore.syncValue();
50078                         }
50079                     }
50080
50081                 }));
50082                 continue;
50083                     
50084                  
50085                 /*
50086                 tb.addField( new Roo.form.TextField({
50087                     name: i,
50088                     width: 100,
50089                     //allowBlank:false,
50090                     value: ''
50091                 }));
50092                 continue;
50093                 */
50094             }
50095             tb.addField( new Roo.form.TextField({
50096                 name: '-roo-edit-' + tlist[i].name,
50097                 attrname : tlist[i].name,
50098                 
50099                 width: item.width,
50100                 //allowBlank:true,
50101                 value: '',
50102                 listeners: {
50103                     'change' : function(f, nv, ov) {
50104                         
50105                         if (tb.selectedNode.hasAttribute('data-block')) {
50106                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50107                             b[f.attrname] = nv;
50108                             b.updateElement(tb.selectedNode);
50109                             editorcore.syncValue();
50110                             return;
50111                         }
50112                         
50113                         tb.selectedNode.setAttribute(f.attrname, nv);
50114                         editorcore.syncValue();
50115                     }
50116                 }
50117             }));
50118              
50119         }
50120         
50121         var _this = this;
50122         
50123         if(nm == 'BODY'){
50124             tb.addSeparator();
50125         
50126             tb.addButton( {
50127                 text: 'Stylesheets',
50128
50129                 listeners : {
50130                     click : function ()
50131                     {
50132                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50133                     }
50134                 }
50135             });
50136         }
50137         
50138         tb.addFill();
50139         tb.addButton({
50140             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50141     
50142             listeners : {
50143                 click : function ()
50144                 {
50145                     var sn = tb.selectedNode;
50146                     if (block) {
50147                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50148                         
50149                     }
50150                     if (!sn) {
50151                         return;
50152                     }
50153                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50154                     if (sn.hasAttribute('data-block')) {
50155                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50156                         sn.parentNode.removeChild(sn);
50157                         
50158                     } else if (sn && sn.tagName != 'BODY') {
50159                         // remove and keep parents.
50160                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50161                         a.removeTag(sn);
50162                     }
50163                     
50164                     
50165                     var range = editorcore.createRange();
50166         
50167                     range.setStart(stn,0);
50168                     range.setEnd(stn,0); 
50169                     var selection = editorcore.getSelection();
50170                     selection.removeAllRanges();
50171                     selection.addRange(range);
50172                     
50173                     
50174                     //_this.updateToolbar(null, null, pn);
50175                     _this.updateToolbar(null, null, null);
50176                     _this.updateFooter(false);
50177                     
50178                 }
50179             }
50180             
50181                     
50182                 
50183             
50184         });
50185         
50186         
50187         tb.el.on('click', function(e){
50188             e.preventDefault(); // what does this do?
50189         });
50190         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50191         tb.el.hide();
50192         
50193         // dont need to disable them... as they will get hidden
50194         return tb;
50195          
50196         
50197     },
50198     buildFooter : function()
50199     {
50200         
50201         var fel = this.editor.wrap.createChild();
50202         this.footer = new Roo.Toolbar(fel);
50203         // toolbar has scrolly on left / right?
50204         var footDisp= new Roo.Toolbar.Fill();
50205         var _t = this;
50206         this.footer.add(
50207             {
50208                 text : '&lt;',
50209                 xtype: 'Button',
50210                 handler : function() {
50211                     _t.footDisp.scrollTo('left',0,true)
50212                 }
50213             }
50214         );
50215         this.footer.add( footDisp );
50216         this.footer.add( 
50217             {
50218                 text : '&gt;',
50219                 xtype: 'Button',
50220                 handler : function() {
50221                     // no animation..
50222                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50223                 }
50224             }
50225         );
50226         var fel = Roo.get(footDisp.el);
50227         fel.addClass('x-editor-context');
50228         this.footDispWrap = fel; 
50229         this.footDispWrap.overflow  = 'hidden';
50230         
50231         this.footDisp = fel.createChild();
50232         this.footDispWrap.on('click', this.onContextClick, this)
50233         
50234         
50235     },
50236     // when the footer contect changes
50237     onContextClick : function (ev,dom)
50238     {
50239         ev.preventDefault();
50240         var  cn = dom.className;
50241         //Roo.log(cn);
50242         if (!cn.match(/x-ed-loc-/)) {
50243             return;
50244         }
50245         var n = cn.split('-').pop();
50246         var ans = this.footerEls;
50247         var sel = ans[n];
50248         
50249         this.editorcore.selectNode(sel);
50250         
50251         
50252         this.updateToolbar(null, null, sel);
50253         
50254         
50255     }
50256     
50257     
50258     
50259     
50260     
50261 });
50262
50263
50264
50265
50266
50267 /*
50268  * Based on:
50269  * Ext JS Library 1.1.1
50270  * Copyright(c) 2006-2007, Ext JS, LLC.
50271  *
50272  * Originally Released Under LGPL - original licence link has changed is not relivant.
50273  *
50274  * Fork - LGPL
50275  * <script type="text/javascript">
50276  */
50277  
50278 /**
50279  * @class Roo.form.BasicForm
50280  * @extends Roo.util.Observable
50281  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50282  * @constructor
50283  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50284  * @param {Object} config Configuration options
50285  */
50286 Roo.form.BasicForm = function(el, config){
50287     this.allItems = [];
50288     this.childForms = [];
50289     Roo.apply(this, config);
50290     /*
50291      * The Roo.form.Field items in this form.
50292      * @type MixedCollection
50293      */
50294      
50295      
50296     this.items = new Roo.util.MixedCollection(false, function(o){
50297         return o.id || (o.id = Roo.id());
50298     });
50299     this.addEvents({
50300         /**
50301          * @event beforeaction
50302          * Fires before any action is performed. Return false to cancel the action.
50303          * @param {Form} this
50304          * @param {Action} action The action to be performed
50305          */
50306         beforeaction: true,
50307         /**
50308          * @event actionfailed
50309          * Fires when an action fails.
50310          * @param {Form} this
50311          * @param {Action} action The action that failed
50312          */
50313         actionfailed : true,
50314         /**
50315          * @event actioncomplete
50316          * Fires when an action is completed.
50317          * @param {Form} this
50318          * @param {Action} action The action that completed
50319          */
50320         actioncomplete : true
50321     });
50322     if(el){
50323         this.initEl(el);
50324     }
50325     Roo.form.BasicForm.superclass.constructor.call(this);
50326     
50327     Roo.form.BasicForm.popover.apply();
50328 };
50329
50330 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50331     /**
50332      * @cfg {String} method
50333      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50334      */
50335     /**
50336      * @cfg {DataReader} reader
50337      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50338      * This is optional as there is built-in support for processing JSON.
50339      */
50340     /**
50341      * @cfg {DataReader} errorReader
50342      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50343      * This is completely optional as there is built-in support for processing JSON.
50344      */
50345     /**
50346      * @cfg {String} url
50347      * The URL to use for form actions if one isn't supplied in the action options.
50348      */
50349     /**
50350      * @cfg {Boolean} fileUpload
50351      * Set to true if this form is a file upload.
50352      */
50353      
50354     /**
50355      * @cfg {Object} baseParams
50356      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50357      */
50358      /**
50359      
50360     /**
50361      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50362      */
50363     timeout: 30,
50364
50365     // private
50366     activeAction : null,
50367
50368     /**
50369      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50370      * or setValues() data instead of when the form was first created.
50371      */
50372     trackResetOnLoad : false,
50373     
50374     
50375     /**
50376      * childForms - used for multi-tab forms
50377      * @type {Array}
50378      */
50379     childForms : false,
50380     
50381     /**
50382      * allItems - full list of fields.
50383      * @type {Array}
50384      */
50385     allItems : false,
50386     
50387     /**
50388      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50389      * element by passing it or its id or mask the form itself by passing in true.
50390      * @type Mixed
50391      */
50392     waitMsgTarget : false,
50393     
50394     /**
50395      * @type Boolean
50396      */
50397     disableMask : false,
50398     
50399     /**
50400      * @cfg {Boolean} errorMask (true|false) default false
50401      */
50402     errorMask : false,
50403     
50404     /**
50405      * @cfg {Number} maskOffset Default 100
50406      */
50407     maskOffset : 100,
50408
50409     // private
50410     initEl : function(el){
50411         this.el = Roo.get(el);
50412         this.id = this.el.id || Roo.id();
50413         this.el.on('submit', this.onSubmit, this);
50414         this.el.addClass('x-form');
50415     },
50416
50417     // private
50418     onSubmit : function(e){
50419         e.stopEvent();
50420     },
50421
50422     /**
50423      * Returns true if client-side validation on the form is successful.
50424      * @return Boolean
50425      */
50426     isValid : function(){
50427         var valid = true;
50428         var target = false;
50429         this.items.each(function(f){
50430             if(f.validate()){
50431                 return;
50432             }
50433             
50434             valid = false;
50435                 
50436             if(!target && f.el.isVisible(true)){
50437                 target = f;
50438             }
50439         });
50440         
50441         if(this.errorMask && !valid){
50442             Roo.form.BasicForm.popover.mask(this, target);
50443         }
50444         
50445         return valid;
50446     },
50447     /**
50448      * Returns array of invalid form fields.
50449      * @return Array
50450      */
50451     
50452     invalidFields : function()
50453     {
50454         var ret = [];
50455         this.items.each(function(f){
50456             if(f.validate()){
50457                 return;
50458             }
50459             ret.push(f);
50460             
50461         });
50462         
50463         return ret;
50464     },
50465     
50466     
50467     /**
50468      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50469      * @return Boolean
50470      */
50471     isDirty : function(){
50472         var dirty = false;
50473         this.items.each(function(f){
50474            if(f.isDirty()){
50475                dirty = true;
50476                return false;
50477            }
50478         });
50479         return dirty;
50480     },
50481     
50482     /**
50483      * Returns true if any fields in this form have changed since their original load. (New version)
50484      * @return Boolean
50485      */
50486     
50487     hasChanged : function()
50488     {
50489         var dirty = false;
50490         this.items.each(function(f){
50491            if(f.hasChanged()){
50492                dirty = true;
50493                return false;
50494            }
50495         });
50496         return dirty;
50497         
50498     },
50499     /**
50500      * Resets all hasChanged to 'false' -
50501      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50502      * So hasChanged storage is only to be used for this purpose
50503      * @return Boolean
50504      */
50505     resetHasChanged : function()
50506     {
50507         this.items.each(function(f){
50508            f.resetHasChanged();
50509         });
50510         
50511     },
50512     
50513     
50514     /**
50515      * Performs a predefined action (submit or load) or custom actions you define on this form.
50516      * @param {String} actionName The name of the action type
50517      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50518      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50519      * accept other config options):
50520      * <pre>
50521 Property          Type             Description
50522 ----------------  ---------------  ----------------------------------------------------------------------------------
50523 url               String           The url for the action (defaults to the form's url)
50524 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50525 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50526 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50527                                    validate the form on the client (defaults to false)
50528      * </pre>
50529      * @return {BasicForm} this
50530      */
50531     doAction : function(action, options){
50532         if(typeof action == 'string'){
50533             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50534         }
50535         if(this.fireEvent('beforeaction', this, action) !== false){
50536             this.beforeAction(action);
50537             action.run.defer(100, action);
50538         }
50539         return this;
50540     },
50541
50542     /**
50543      * Shortcut to do a submit action.
50544      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50545      * @return {BasicForm} this
50546      */
50547     submit : function(options){
50548         this.doAction('submit', options);
50549         return this;
50550     },
50551
50552     /**
50553      * Shortcut to do a load action.
50554      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50555      * @return {BasicForm} this
50556      */
50557     load : function(options){
50558         this.doAction('load', options);
50559         return this;
50560     },
50561
50562     /**
50563      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50564      * @param {Record} record The record to edit
50565      * @return {BasicForm} this
50566      */
50567     updateRecord : function(record){
50568         record.beginEdit();
50569         var fs = record.fields;
50570         fs.each(function(f){
50571             var field = this.findField(f.name);
50572             if(field){
50573                 record.set(f.name, field.getValue());
50574             }
50575         }, this);
50576         record.endEdit();
50577         return this;
50578     },
50579
50580     /**
50581      * Loads an Roo.data.Record into this form.
50582      * @param {Record} record The record to load
50583      * @return {BasicForm} this
50584      */
50585     loadRecord : function(record){
50586         this.setValues(record.data);
50587         return this;
50588     },
50589
50590     // private
50591     beforeAction : function(action){
50592         var o = action.options;
50593         
50594         if(!this.disableMask) {
50595             if(this.waitMsgTarget === true){
50596                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50597             }else if(this.waitMsgTarget){
50598                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50599                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50600             }else {
50601                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50602             }
50603         }
50604         
50605          
50606     },
50607
50608     // private
50609     afterAction : function(action, success){
50610         this.activeAction = null;
50611         var o = action.options;
50612         
50613         if(!this.disableMask) {
50614             if(this.waitMsgTarget === true){
50615                 this.el.unmask();
50616             }else if(this.waitMsgTarget){
50617                 this.waitMsgTarget.unmask();
50618             }else{
50619                 Roo.MessageBox.updateProgress(1);
50620                 Roo.MessageBox.hide();
50621             }
50622         }
50623         
50624         if(success){
50625             if(o.reset){
50626                 this.reset();
50627             }
50628             Roo.callback(o.success, o.scope, [this, action]);
50629             this.fireEvent('actioncomplete', this, action);
50630             
50631         }else{
50632             
50633             // failure condition..
50634             // we have a scenario where updates need confirming.
50635             // eg. if a locking scenario exists..
50636             // we look for { errors : { needs_confirm : true }} in the response.
50637             if (
50638                 (typeof(action.result) != 'undefined')  &&
50639                 (typeof(action.result.errors) != 'undefined')  &&
50640                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50641            ){
50642                 var _t = this;
50643                 Roo.MessageBox.confirm(
50644                     "Change requires confirmation",
50645                     action.result.errorMsg,
50646                     function(r) {
50647                         if (r != 'yes') {
50648                             return;
50649                         }
50650                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50651                     }
50652                     
50653                 );
50654                 
50655                 
50656                 
50657                 return;
50658             }
50659             
50660             Roo.callback(o.failure, o.scope, [this, action]);
50661             // show an error message if no failed handler is set..
50662             if (!this.hasListener('actionfailed')) {
50663                 Roo.MessageBox.alert("Error",
50664                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50665                         action.result.errorMsg :
50666                         "Saving Failed, please check your entries or try again"
50667                 );
50668             }
50669             
50670             this.fireEvent('actionfailed', this, action);
50671         }
50672         
50673     },
50674
50675     /**
50676      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50677      * @param {String} id The value to search for
50678      * @return Field
50679      */
50680     findField : function(id){
50681         var field = this.items.get(id);
50682         if(!field){
50683             this.items.each(function(f){
50684                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50685                     field = f;
50686                     return false;
50687                 }
50688             });
50689         }
50690         return field || null;
50691     },
50692
50693     /**
50694      * Add a secondary form to this one, 
50695      * Used to provide tabbed forms. One form is primary, with hidden values 
50696      * which mirror the elements from the other forms.
50697      * 
50698      * @param {Roo.form.Form} form to add.
50699      * 
50700      */
50701     addForm : function(form)
50702     {
50703        
50704         if (this.childForms.indexOf(form) > -1) {
50705             // already added..
50706             return;
50707         }
50708         this.childForms.push(form);
50709         var n = '';
50710         Roo.each(form.allItems, function (fe) {
50711             
50712             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50713             if (this.findField(n)) { // already added..
50714                 return;
50715             }
50716             var add = new Roo.form.Hidden({
50717                 name : n
50718             });
50719             add.render(this.el);
50720             
50721             this.add( add );
50722         }, this);
50723         
50724     },
50725     /**
50726      * Mark fields in this form invalid in bulk.
50727      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50728      * @return {BasicForm} this
50729      */
50730     markInvalid : function(errors){
50731         if(errors instanceof Array){
50732             for(var i = 0, len = errors.length; i < len; i++){
50733                 var fieldError = errors[i];
50734                 var f = this.findField(fieldError.id);
50735                 if(f){
50736                     f.markInvalid(fieldError.msg);
50737                 }
50738             }
50739         }else{
50740             var field, id;
50741             for(id in errors){
50742                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50743                     field.markInvalid(errors[id]);
50744                 }
50745             }
50746         }
50747         Roo.each(this.childForms || [], function (f) {
50748             f.markInvalid(errors);
50749         });
50750         
50751         return this;
50752     },
50753
50754     /**
50755      * Set values for fields in this form in bulk.
50756      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50757      * @return {BasicForm} this
50758      */
50759     setValues : function(values){
50760         if(values instanceof Array){ // array of objects
50761             for(var i = 0, len = values.length; i < len; i++){
50762                 var v = values[i];
50763                 var f = this.findField(v.id);
50764                 if(f){
50765                     f.setValue(v.value);
50766                     if(this.trackResetOnLoad){
50767                         f.originalValue = f.getValue();
50768                     }
50769                 }
50770             }
50771         }else{ // object hash
50772             var field, id;
50773             for(id in values){
50774                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50775                     
50776                     if (field.setFromData && 
50777                         field.valueField && 
50778                         field.displayField &&
50779                         // combos' with local stores can 
50780                         // be queried via setValue()
50781                         // to set their value..
50782                         (field.store && !field.store.isLocal)
50783                         ) {
50784                         // it's a combo
50785                         var sd = { };
50786                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50787                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50788                         field.setFromData(sd);
50789                         
50790                     } else {
50791                         field.setValue(values[id]);
50792                     }
50793                     
50794                     
50795                     if(this.trackResetOnLoad){
50796                         field.originalValue = field.getValue();
50797                     }
50798                 }
50799             }
50800         }
50801         this.resetHasChanged();
50802         
50803         
50804         Roo.each(this.childForms || [], function (f) {
50805             f.setValues(values);
50806             f.resetHasChanged();
50807         });
50808                 
50809         return this;
50810     },
50811  
50812     /**
50813      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50814      * they are returned as an array.
50815      * @param {Boolean} asString
50816      * @return {Object}
50817      */
50818     getValues : function(asString)
50819     {
50820         if (this.childForms) {
50821             // copy values from the child forms
50822             Roo.each(this.childForms, function (f) {
50823                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50824             }, this);
50825         }
50826         
50827         // use formdata
50828         if (typeof(FormData) != 'undefined' && asString !== true) {
50829             // this relies on a 'recent' version of chrome apparently...
50830             try {
50831                 var fd = (new FormData(this.el.dom)).entries();
50832                 var ret = {};
50833                 var ent = fd.next();
50834                 while (!ent.done) {
50835                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50836                     ent = fd.next();
50837                 };
50838                 return ret;
50839             } catch(e) {
50840                 
50841             }
50842             
50843         }
50844         
50845         
50846         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50847         if(asString === true){
50848             return fs;
50849         }
50850         return Roo.urlDecode(fs);
50851     },
50852     
50853     /**
50854      * Returns the fields in this form as an object with key/value pairs. 
50855      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50856      * Normally this will not return readOnly data 
50857      * @param {Boolean} with_readonly return readonly field data.
50858      * @return {Object}
50859      */
50860     getFieldValues : function(with_readonly)
50861     {
50862         if (this.childForms) {
50863             // copy values from the child forms
50864             // should this call getFieldValues - probably not as we do not currently copy
50865             // hidden fields when we generate..
50866             Roo.each(this.childForms, function (f) {
50867                 this.setValues(f.getFieldValues());
50868             }, this);
50869         }
50870         
50871         var ret = {};
50872         this.items.each(function(f){
50873             
50874             if (f.readOnly && with_readonly !== true) {
50875                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50876                         // if a subform contains a copy of them.
50877                         // if you have subforms with the same editable data, you will need to copy the data back
50878                         // and forth.
50879             }
50880             
50881             if (!f.getName()) {
50882                 return;
50883             }
50884             var v = f.getValue();
50885             if (f.inputType =='radio') {
50886                 if (typeof(ret[f.getName()]) == 'undefined') {
50887                     ret[f.getName()] = ''; // empty..
50888                 }
50889                 
50890                 if (!f.el.dom.checked) {
50891                     return;
50892                     
50893                 }
50894                 v = f.el.dom.value;
50895                 
50896             }
50897             
50898             // not sure if this supported any more..
50899             if ((typeof(v) == 'object') && f.getRawValue) {
50900                 v = f.getRawValue() ; // dates..
50901             }
50902             // combo boxes where name != hiddenName...
50903             if (f.name != f.getName()) {
50904                 ret[f.name] = f.getRawValue();
50905             }
50906             ret[f.getName()] = v;
50907         });
50908         
50909         return ret;
50910     },
50911
50912     /**
50913      * Clears all invalid messages in this form.
50914      * @return {BasicForm} this
50915      */
50916     clearInvalid : function(){
50917         this.items.each(function(f){
50918            f.clearInvalid();
50919         });
50920         
50921         Roo.each(this.childForms || [], function (f) {
50922             f.clearInvalid();
50923         });
50924         
50925         
50926         return this;
50927     },
50928
50929     /**
50930      * Resets this form.
50931      * @return {BasicForm} this
50932      */
50933     reset : function(){
50934         this.items.each(function(f){
50935             f.reset();
50936         });
50937         
50938         Roo.each(this.childForms || [], function (f) {
50939             f.reset();
50940         });
50941         this.resetHasChanged();
50942         
50943         return this;
50944     },
50945
50946     /**
50947      * Add Roo.form components to this form.
50948      * @param {Field} field1
50949      * @param {Field} field2 (optional)
50950      * @param {Field} etc (optional)
50951      * @return {BasicForm} this
50952      */
50953     add : function(){
50954         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50955         return this;
50956     },
50957
50958
50959     /**
50960      * Removes a field from the items collection (does NOT remove its markup).
50961      * @param {Field} field
50962      * @return {BasicForm} this
50963      */
50964     remove : function(field){
50965         this.items.remove(field);
50966         return this;
50967     },
50968
50969     /**
50970      * Looks at the fields in this form, checks them for an id attribute,
50971      * and calls applyTo on the existing dom element with that id.
50972      * @return {BasicForm} this
50973      */
50974     render : function(){
50975         this.items.each(function(f){
50976             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50977                 f.applyTo(f.id);
50978             }
50979         });
50980         return this;
50981     },
50982
50983     /**
50984      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50985      * @param {Object} values
50986      * @return {BasicForm} this
50987      */
50988     applyToFields : function(o){
50989         this.items.each(function(f){
50990            Roo.apply(f, o);
50991         });
50992         return this;
50993     },
50994
50995     /**
50996      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50997      * @param {Object} values
50998      * @return {BasicForm} this
50999      */
51000     applyIfToFields : function(o){
51001         this.items.each(function(f){
51002            Roo.applyIf(f, o);
51003         });
51004         return this;
51005     }
51006 });
51007
51008 // back compat
51009 Roo.BasicForm = Roo.form.BasicForm;
51010
51011 Roo.apply(Roo.form.BasicForm, {
51012     
51013     popover : {
51014         
51015         padding : 5,
51016         
51017         isApplied : false,
51018         
51019         isMasked : false,
51020         
51021         form : false,
51022         
51023         target : false,
51024         
51025         intervalID : false,
51026         
51027         maskEl : false,
51028         
51029         apply : function()
51030         {
51031             if(this.isApplied){
51032                 return;
51033             }
51034             
51035             this.maskEl = {
51036                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51037                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51038                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51039                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51040             };
51041             
51042             this.maskEl.top.enableDisplayMode("block");
51043             this.maskEl.left.enableDisplayMode("block");
51044             this.maskEl.bottom.enableDisplayMode("block");
51045             this.maskEl.right.enableDisplayMode("block");
51046             
51047             Roo.get(document.body).on('click', function(){
51048                 this.unmask();
51049             }, this);
51050             
51051             Roo.get(document.body).on('touchstart', function(){
51052                 this.unmask();
51053             }, this);
51054             
51055             this.isApplied = true
51056         },
51057         
51058         mask : function(form, target)
51059         {
51060             this.form = form;
51061             
51062             this.target = target;
51063             
51064             if(!this.form.errorMask || !target.el){
51065                 return;
51066             }
51067             
51068             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51069             
51070             var ot = this.target.el.calcOffsetsTo(scrollable);
51071             
51072             var scrollTo = ot[1] - this.form.maskOffset;
51073             
51074             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51075             
51076             scrollable.scrollTo('top', scrollTo);
51077             
51078             var el = this.target.wrap || this.target.el;
51079             
51080             var box = el.getBox();
51081             
51082             this.maskEl.top.setStyle('position', 'absolute');
51083             this.maskEl.top.setStyle('z-index', 10000);
51084             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51085             this.maskEl.top.setLeft(0);
51086             this.maskEl.top.setTop(0);
51087             this.maskEl.top.show();
51088             
51089             this.maskEl.left.setStyle('position', 'absolute');
51090             this.maskEl.left.setStyle('z-index', 10000);
51091             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51092             this.maskEl.left.setLeft(0);
51093             this.maskEl.left.setTop(box.y - this.padding);
51094             this.maskEl.left.show();
51095
51096             this.maskEl.bottom.setStyle('position', 'absolute');
51097             this.maskEl.bottom.setStyle('z-index', 10000);
51098             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51099             this.maskEl.bottom.setLeft(0);
51100             this.maskEl.bottom.setTop(box.bottom + this.padding);
51101             this.maskEl.bottom.show();
51102
51103             this.maskEl.right.setStyle('position', 'absolute');
51104             this.maskEl.right.setStyle('z-index', 10000);
51105             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51106             this.maskEl.right.setLeft(box.right + this.padding);
51107             this.maskEl.right.setTop(box.y - this.padding);
51108             this.maskEl.right.show();
51109
51110             this.intervalID = window.setInterval(function() {
51111                 Roo.form.BasicForm.popover.unmask();
51112             }, 10000);
51113
51114             window.onwheel = function(){ return false;};
51115             
51116             (function(){ this.isMasked = true; }).defer(500, this);
51117             
51118         },
51119         
51120         unmask : function()
51121         {
51122             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51123                 return;
51124             }
51125             
51126             this.maskEl.top.setStyle('position', 'absolute');
51127             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51128             this.maskEl.top.hide();
51129
51130             this.maskEl.left.setStyle('position', 'absolute');
51131             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51132             this.maskEl.left.hide();
51133
51134             this.maskEl.bottom.setStyle('position', 'absolute');
51135             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51136             this.maskEl.bottom.hide();
51137
51138             this.maskEl.right.setStyle('position', 'absolute');
51139             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51140             this.maskEl.right.hide();
51141             
51142             window.onwheel = function(){ return true;};
51143             
51144             if(this.intervalID){
51145                 window.clearInterval(this.intervalID);
51146                 this.intervalID = false;
51147             }
51148             
51149             this.isMasked = false;
51150             
51151         }
51152         
51153     }
51154     
51155 });/*
51156  * Based on:
51157  * Ext JS Library 1.1.1
51158  * Copyright(c) 2006-2007, Ext JS, LLC.
51159  *
51160  * Originally Released Under LGPL - original licence link has changed is not relivant.
51161  *
51162  * Fork - LGPL
51163  * <script type="text/javascript">
51164  */
51165
51166 /**
51167  * @class Roo.form.Form
51168  * @extends Roo.form.BasicForm
51169  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51170  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51171  * @constructor
51172  * @param {Object} config Configuration options
51173  */
51174 Roo.form.Form = function(config){
51175     var xitems =  [];
51176     if (config.items) {
51177         xitems = config.items;
51178         delete config.items;
51179     }
51180    
51181     
51182     Roo.form.Form.superclass.constructor.call(this, null, config);
51183     this.url = this.url || this.action;
51184     if(!this.root){
51185         this.root = new Roo.form.Layout(Roo.applyIf({
51186             id: Roo.id()
51187         }, config));
51188     }
51189     this.active = this.root;
51190     /**
51191      * Array of all the buttons that have been added to this form via {@link addButton}
51192      * @type Array
51193      */
51194     this.buttons = [];
51195     this.allItems = [];
51196     this.addEvents({
51197         /**
51198          * @event clientvalidation
51199          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51200          * @param {Form} this
51201          * @param {Boolean} valid true if the form has passed client-side validation
51202          */
51203         clientvalidation: true,
51204         /**
51205          * @event rendered
51206          * Fires when the form is rendered
51207          * @param {Roo.form.Form} form
51208          */
51209         rendered : true
51210     });
51211     
51212     if (this.progressUrl) {
51213             // push a hidden field onto the list of fields..
51214             this.addxtype( {
51215                     xns: Roo.form, 
51216                     xtype : 'Hidden', 
51217                     name : 'UPLOAD_IDENTIFIER' 
51218             });
51219         }
51220         
51221     
51222     Roo.each(xitems, this.addxtype, this);
51223     
51224 };
51225
51226 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51227      /**
51228      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51229      */
51230     
51231     /**
51232      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51233      */
51234     /**
51235      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51236      */
51237     /**
51238      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51239      */
51240     buttonAlign:'center',
51241
51242     /**
51243      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51244      */
51245     minButtonWidth:75,
51246
51247     /**
51248      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51249      * This property cascades to child containers if not set.
51250      */
51251     labelAlign:'left',
51252
51253     /**
51254      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51255      * fires a looping event with that state. This is required to bind buttons to the valid
51256      * state using the config value formBind:true on the button.
51257      */
51258     monitorValid : false,
51259
51260     /**
51261      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51262      */
51263     monitorPoll : 200,
51264     
51265     /**
51266      * @cfg {String} progressUrl - Url to return progress data 
51267      */
51268     
51269     progressUrl : false,
51270     /**
51271      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51272      * sending a formdata with extra parameters - eg uploaded elements.
51273      */
51274     
51275     formData : false,
51276     
51277     /**
51278      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51279      * fields are added and the column is closed. If no fields are passed the column remains open
51280      * until end() is called.
51281      * @param {Object} config The config to pass to the column
51282      * @param {Field} field1 (optional)
51283      * @param {Field} field2 (optional)
51284      * @param {Field} etc (optional)
51285      * @return Column The column container object
51286      */
51287     column : function(c){
51288         var col = new Roo.form.Column(c);
51289         this.start(col);
51290         if(arguments.length > 1){ // duplicate code required because of Opera
51291             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51292             this.end();
51293         }
51294         return col;
51295     },
51296
51297     /**
51298      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51299      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51300      * until end() is called.
51301      * @param {Object} config The config to pass to the fieldset
51302      * @param {Field} field1 (optional)
51303      * @param {Field} field2 (optional)
51304      * @param {Field} etc (optional)
51305      * @return FieldSet The fieldset container object
51306      */
51307     fieldset : function(c){
51308         var fs = new Roo.form.FieldSet(c);
51309         this.start(fs);
51310         if(arguments.length > 1){ // duplicate code required because of Opera
51311             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51312             this.end();
51313         }
51314         return fs;
51315     },
51316
51317     /**
51318      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51319      * fields are added and the container is closed. If no fields are passed the container remains open
51320      * until end() is called.
51321      * @param {Object} config The config to pass to the Layout
51322      * @param {Field} field1 (optional)
51323      * @param {Field} field2 (optional)
51324      * @param {Field} etc (optional)
51325      * @return Layout The container object
51326      */
51327     container : function(c){
51328         var l = new Roo.form.Layout(c);
51329         this.start(l);
51330         if(arguments.length > 1){ // duplicate code required because of Opera
51331             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51332             this.end();
51333         }
51334         return l;
51335     },
51336
51337     /**
51338      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51339      * @param {Object} container A Roo.form.Layout or subclass of Layout
51340      * @return {Form} this
51341      */
51342     start : function(c){
51343         // cascade label info
51344         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51345         this.active.stack.push(c);
51346         c.ownerCt = this.active;
51347         this.active = c;
51348         return this;
51349     },
51350
51351     /**
51352      * Closes the current open container
51353      * @return {Form} this
51354      */
51355     end : function(){
51356         if(this.active == this.root){
51357             return this;
51358         }
51359         this.active = this.active.ownerCt;
51360         return this;
51361     },
51362
51363     /**
51364      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51365      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51366      * as the label of the field.
51367      * @param {Field} field1
51368      * @param {Field} field2 (optional)
51369      * @param {Field} etc. (optional)
51370      * @return {Form} this
51371      */
51372     add : function(){
51373         this.active.stack.push.apply(this.active.stack, arguments);
51374         this.allItems.push.apply(this.allItems,arguments);
51375         var r = [];
51376         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51377             if(a[i].isFormField){
51378                 r.push(a[i]);
51379             }
51380         }
51381         if(r.length > 0){
51382             Roo.form.Form.superclass.add.apply(this, r);
51383         }
51384         return this;
51385     },
51386     
51387
51388     
51389     
51390     
51391      /**
51392      * Find any element that has been added to a form, using it's ID or name
51393      * This can include framesets, columns etc. along with regular fields..
51394      * @param {String} id - id or name to find.
51395      
51396      * @return {Element} e - or false if nothing found.
51397      */
51398     findbyId : function(id)
51399     {
51400         var ret = false;
51401         if (!id) {
51402             return ret;
51403         }
51404         Roo.each(this.allItems, function(f){
51405             if (f.id == id || f.name == id ){
51406                 ret = f;
51407                 return false;
51408             }
51409         });
51410         return ret;
51411     },
51412
51413     
51414     
51415     /**
51416      * Render this form into the passed container. This should only be called once!
51417      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51418      * @return {Form} this
51419      */
51420     render : function(ct)
51421     {
51422         
51423         
51424         
51425         ct = Roo.get(ct);
51426         var o = this.autoCreate || {
51427             tag: 'form',
51428             method : this.method || 'POST',
51429             id : this.id || Roo.id()
51430         };
51431         this.initEl(ct.createChild(o));
51432
51433         this.root.render(this.el);
51434         
51435        
51436              
51437         this.items.each(function(f){
51438             f.render('x-form-el-'+f.id);
51439         });
51440
51441         if(this.buttons.length > 0){
51442             // tables are required to maintain order and for correct IE layout
51443             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51444                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51445                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51446             }}, null, true);
51447             var tr = tb.getElementsByTagName('tr')[0];
51448             for(var i = 0, len = this.buttons.length; i < len; i++) {
51449                 var b = this.buttons[i];
51450                 var td = document.createElement('td');
51451                 td.className = 'x-form-btn-td';
51452                 b.render(tr.appendChild(td));
51453             }
51454         }
51455         if(this.monitorValid){ // initialize after render
51456             this.startMonitoring();
51457         }
51458         this.fireEvent('rendered', this);
51459         return this;
51460     },
51461
51462     /**
51463      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51464      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51465      * object or a valid Roo.DomHelper element config
51466      * @param {Function} handler The function called when the button is clicked
51467      * @param {Object} scope (optional) The scope of the handler function
51468      * @return {Roo.Button}
51469      */
51470     addButton : function(config, handler, scope){
51471         var bc = {
51472             handler: handler,
51473             scope: scope,
51474             minWidth: this.minButtonWidth,
51475             hideParent:true
51476         };
51477         if(typeof config == "string"){
51478             bc.text = config;
51479         }else{
51480             Roo.apply(bc, config);
51481         }
51482         var btn = new Roo.Button(null, bc);
51483         this.buttons.push(btn);
51484         return btn;
51485     },
51486
51487      /**
51488      * Adds a series of form elements (using the xtype property as the factory method.
51489      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51490      * @param {Object} config 
51491      */
51492     
51493     addxtype : function()
51494     {
51495         var ar = Array.prototype.slice.call(arguments, 0);
51496         var ret = false;
51497         for(var i = 0; i < ar.length; i++) {
51498             if (!ar[i]) {
51499                 continue; // skip -- if this happends something invalid got sent, we 
51500                 // should ignore it, as basically that interface element will not show up
51501                 // and that should be pretty obvious!!
51502             }
51503             
51504             if (Roo.form[ar[i].xtype]) {
51505                 ar[i].form = this;
51506                 var fe = Roo.factory(ar[i], Roo.form);
51507                 if (!ret) {
51508                     ret = fe;
51509                 }
51510                 fe.form = this;
51511                 if (fe.store) {
51512                     fe.store.form = this;
51513                 }
51514                 if (fe.isLayout) {  
51515                          
51516                     this.start(fe);
51517                     this.allItems.push(fe);
51518                     if (fe.items && fe.addxtype) {
51519                         fe.addxtype.apply(fe, fe.items);
51520                         delete fe.items;
51521                     }
51522                      this.end();
51523                     continue;
51524                 }
51525                 
51526                 
51527                  
51528                 this.add(fe);
51529               //  console.log('adding ' + ar[i].xtype);
51530             }
51531             if (ar[i].xtype == 'Button') {  
51532                 //console.log('adding button');
51533                 //console.log(ar[i]);
51534                 this.addButton(ar[i]);
51535                 this.allItems.push(fe);
51536                 continue;
51537             }
51538             
51539             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51540                 alert('end is not supported on xtype any more, use items');
51541             //    this.end();
51542             //    //console.log('adding end');
51543             }
51544             
51545         }
51546         return ret;
51547     },
51548     
51549     /**
51550      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51551      * option "monitorValid"
51552      */
51553     startMonitoring : function(){
51554         if(!this.bound){
51555             this.bound = true;
51556             Roo.TaskMgr.start({
51557                 run : this.bindHandler,
51558                 interval : this.monitorPoll || 200,
51559                 scope: this
51560             });
51561         }
51562     },
51563
51564     /**
51565      * Stops monitoring of the valid state of this form
51566      */
51567     stopMonitoring : function(){
51568         this.bound = false;
51569     },
51570
51571     // private
51572     bindHandler : function(){
51573         if(!this.bound){
51574             return false; // stops binding
51575         }
51576         var valid = true;
51577         this.items.each(function(f){
51578             if(!f.isValid(true)){
51579                 valid = false;
51580                 return false;
51581             }
51582         });
51583         for(var i = 0, len = this.buttons.length; i < len; i++){
51584             var btn = this.buttons[i];
51585             if(btn.formBind === true && btn.disabled === valid){
51586                 btn.setDisabled(!valid);
51587             }
51588         }
51589         this.fireEvent('clientvalidation', this, valid);
51590     }
51591     
51592     
51593     
51594     
51595     
51596     
51597     
51598     
51599 });
51600
51601
51602 // back compat
51603 Roo.Form = Roo.form.Form;
51604 /*
51605  * Based on:
51606  * Ext JS Library 1.1.1
51607  * Copyright(c) 2006-2007, Ext JS, LLC.
51608  *
51609  * Originally Released Under LGPL - original licence link has changed is not relivant.
51610  *
51611  * Fork - LGPL
51612  * <script type="text/javascript">
51613  */
51614
51615 // as we use this in bootstrap.
51616 Roo.namespace('Roo.form');
51617  /**
51618  * @class Roo.form.Action
51619  * Internal Class used to handle form actions
51620  * @constructor
51621  * @param {Roo.form.BasicForm} el The form element or its id
51622  * @param {Object} config Configuration options
51623  */
51624
51625  
51626  
51627 // define the action interface
51628 Roo.form.Action = function(form, options){
51629     this.form = form;
51630     this.options = options || {};
51631 };
51632 /**
51633  * Client Validation Failed
51634  * @const 
51635  */
51636 Roo.form.Action.CLIENT_INVALID = 'client';
51637 /**
51638  * Server Validation Failed
51639  * @const 
51640  */
51641 Roo.form.Action.SERVER_INVALID = 'server';
51642  /**
51643  * Connect to Server Failed
51644  * @const 
51645  */
51646 Roo.form.Action.CONNECT_FAILURE = 'connect';
51647 /**
51648  * Reading Data from Server Failed
51649  * @const 
51650  */
51651 Roo.form.Action.LOAD_FAILURE = 'load';
51652
51653 Roo.form.Action.prototype = {
51654     type : 'default',
51655     failureType : undefined,
51656     response : undefined,
51657     result : undefined,
51658
51659     // interface method
51660     run : function(options){
51661
51662     },
51663
51664     // interface method
51665     success : function(response){
51666
51667     },
51668
51669     // interface method
51670     handleResponse : function(response){
51671
51672     },
51673
51674     // default connection failure
51675     failure : function(response){
51676         
51677         this.response = response;
51678         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51679         this.form.afterAction(this, false);
51680     },
51681
51682     processResponse : function(response){
51683         this.response = response;
51684         if(!response.responseText){
51685             return true;
51686         }
51687         this.result = this.handleResponse(response);
51688         return this.result;
51689     },
51690
51691     // utility functions used internally
51692     getUrl : function(appendParams){
51693         var url = this.options.url || this.form.url || this.form.el.dom.action;
51694         if(appendParams){
51695             var p = this.getParams();
51696             if(p){
51697                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51698             }
51699         }
51700         return url;
51701     },
51702
51703     getMethod : function(){
51704         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51705     },
51706
51707     getParams : function(){
51708         var bp = this.form.baseParams;
51709         var p = this.options.params;
51710         if(p){
51711             if(typeof p == "object"){
51712                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51713             }else if(typeof p == 'string' && bp){
51714                 p += '&' + Roo.urlEncode(bp);
51715             }
51716         }else if(bp){
51717             p = Roo.urlEncode(bp);
51718         }
51719         return p;
51720     },
51721
51722     createCallback : function(){
51723         return {
51724             success: this.success,
51725             failure: this.failure,
51726             scope: this,
51727             timeout: (this.form.timeout*1000),
51728             upload: this.form.fileUpload ? this.success : undefined
51729         };
51730     }
51731 };
51732
51733 Roo.form.Action.Submit = function(form, options){
51734     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51735 };
51736
51737 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51738     type : 'submit',
51739
51740     haveProgress : false,
51741     uploadComplete : false,
51742     
51743     // uploadProgress indicator.
51744     uploadProgress : function()
51745     {
51746         if (!this.form.progressUrl) {
51747             return;
51748         }
51749         
51750         if (!this.haveProgress) {
51751             Roo.MessageBox.progress("Uploading", "Uploading");
51752         }
51753         if (this.uploadComplete) {
51754            Roo.MessageBox.hide();
51755            return;
51756         }
51757         
51758         this.haveProgress = true;
51759    
51760         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51761         
51762         var c = new Roo.data.Connection();
51763         c.request({
51764             url : this.form.progressUrl,
51765             params: {
51766                 id : uid
51767             },
51768             method: 'GET',
51769             success : function(req){
51770                //console.log(data);
51771                 var rdata = false;
51772                 var edata;
51773                 try  {
51774                    rdata = Roo.decode(req.responseText)
51775                 } catch (e) {
51776                     Roo.log("Invalid data from server..");
51777                     Roo.log(edata);
51778                     return;
51779                 }
51780                 if (!rdata || !rdata.success) {
51781                     Roo.log(rdata);
51782                     Roo.MessageBox.alert(Roo.encode(rdata));
51783                     return;
51784                 }
51785                 var data = rdata.data;
51786                 
51787                 if (this.uploadComplete) {
51788                    Roo.MessageBox.hide();
51789                    return;
51790                 }
51791                    
51792                 if (data){
51793                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51794                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51795                     );
51796                 }
51797                 this.uploadProgress.defer(2000,this);
51798             },
51799        
51800             failure: function(data) {
51801                 Roo.log('progress url failed ');
51802                 Roo.log(data);
51803             },
51804             scope : this
51805         });
51806            
51807     },
51808     
51809     
51810     run : function()
51811     {
51812         // run get Values on the form, so it syncs any secondary forms.
51813         this.form.getValues();
51814         
51815         var o = this.options;
51816         var method = this.getMethod();
51817         var isPost = method == 'POST';
51818         if(o.clientValidation === false || this.form.isValid()){
51819             
51820             if (this.form.progressUrl) {
51821                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51822                     (new Date() * 1) + '' + Math.random());
51823                     
51824             } 
51825             
51826             
51827             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51828                 form:this.form.el.dom,
51829                 url:this.getUrl(!isPost),
51830                 method: method,
51831                 params:isPost ? this.getParams() : null,
51832                 isUpload: this.form.fileUpload,
51833                 formData : this.form.formData
51834             }));
51835             
51836             this.uploadProgress();
51837
51838         }else if (o.clientValidation !== false){ // client validation failed
51839             this.failureType = Roo.form.Action.CLIENT_INVALID;
51840             this.form.afterAction(this, false);
51841         }
51842     },
51843
51844     success : function(response)
51845     {
51846         this.uploadComplete= true;
51847         if (this.haveProgress) {
51848             Roo.MessageBox.hide();
51849         }
51850         
51851         
51852         var result = this.processResponse(response);
51853         if(result === true || result.success){
51854             this.form.afterAction(this, true);
51855             return;
51856         }
51857         if(result.errors){
51858             this.form.markInvalid(result.errors);
51859             this.failureType = Roo.form.Action.SERVER_INVALID;
51860         }
51861         this.form.afterAction(this, false);
51862     },
51863     failure : function(response)
51864     {
51865         this.uploadComplete= true;
51866         if (this.haveProgress) {
51867             Roo.MessageBox.hide();
51868         }
51869         
51870         this.response = response;
51871         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51872         this.form.afterAction(this, false);
51873     },
51874     
51875     handleResponse : function(response){
51876         if(this.form.errorReader){
51877             var rs = this.form.errorReader.read(response);
51878             var errors = [];
51879             if(rs.records){
51880                 for(var i = 0, len = rs.records.length; i < len; i++) {
51881                     var r = rs.records[i];
51882                     errors[i] = r.data;
51883                 }
51884             }
51885             if(errors.length < 1){
51886                 errors = null;
51887             }
51888             return {
51889                 success : rs.success,
51890                 errors : errors
51891             };
51892         }
51893         var ret = false;
51894         try {
51895             ret = Roo.decode(response.responseText);
51896         } catch (e) {
51897             ret = {
51898                 success: false,
51899                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51900                 errors : []
51901             };
51902         }
51903         return ret;
51904         
51905     }
51906 });
51907
51908
51909 Roo.form.Action.Load = function(form, options){
51910     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51911     this.reader = this.form.reader;
51912 };
51913
51914 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51915     type : 'load',
51916
51917     run : function(){
51918         
51919         Roo.Ajax.request(Roo.apply(
51920                 this.createCallback(), {
51921                     method:this.getMethod(),
51922                     url:this.getUrl(false),
51923                     params:this.getParams()
51924         }));
51925     },
51926
51927     success : function(response){
51928         
51929         var result = this.processResponse(response);
51930         if(result === true || !result.success || !result.data){
51931             this.failureType = Roo.form.Action.LOAD_FAILURE;
51932             this.form.afterAction(this, false);
51933             return;
51934         }
51935         this.form.clearInvalid();
51936         this.form.setValues(result.data);
51937         this.form.afterAction(this, true);
51938     },
51939
51940     handleResponse : function(response){
51941         if(this.form.reader){
51942             var rs = this.form.reader.read(response);
51943             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51944             return {
51945                 success : rs.success,
51946                 data : data
51947             };
51948         }
51949         return Roo.decode(response.responseText);
51950     }
51951 });
51952
51953 Roo.form.Action.ACTION_TYPES = {
51954     'load' : Roo.form.Action.Load,
51955     'submit' : Roo.form.Action.Submit
51956 };/*
51957  * Based on:
51958  * Ext JS Library 1.1.1
51959  * Copyright(c) 2006-2007, Ext JS, LLC.
51960  *
51961  * Originally Released Under LGPL - original licence link has changed is not relivant.
51962  *
51963  * Fork - LGPL
51964  * <script type="text/javascript">
51965  */
51966  
51967 /**
51968  * @class Roo.form.Layout
51969  * @extends Roo.Component
51970  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51971  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51972  * @constructor
51973  * @param {Object} config Configuration options
51974  */
51975 Roo.form.Layout = function(config){
51976     var xitems = [];
51977     if (config.items) {
51978         xitems = config.items;
51979         delete config.items;
51980     }
51981     Roo.form.Layout.superclass.constructor.call(this, config);
51982     this.stack = [];
51983     Roo.each(xitems, this.addxtype, this);
51984      
51985 };
51986
51987 Roo.extend(Roo.form.Layout, Roo.Component, {
51988     /**
51989      * @cfg {String/Object} autoCreate
51990      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51991      */
51992     /**
51993      * @cfg {String/Object/Function} style
51994      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51995      * a function which returns such a specification.
51996      */
51997     /**
51998      * @cfg {String} labelAlign
51999      * Valid values are "left," "top" and "right" (defaults to "left")
52000      */
52001     /**
52002      * @cfg {Number} labelWidth
52003      * Fixed width in pixels of all field labels (defaults to undefined)
52004      */
52005     /**
52006      * @cfg {Boolean} clear
52007      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
52008      */
52009     clear : true,
52010     /**
52011      * @cfg {String} labelSeparator
52012      * The separator to use after field labels (defaults to ':')
52013      */
52014     labelSeparator : ':',
52015     /**
52016      * @cfg {Boolean} hideLabels
52017      * True to suppress the display of field labels in this layout (defaults to false)
52018      */
52019     hideLabels : false,
52020
52021     // private
52022     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
52023     
52024     isLayout : true,
52025     
52026     // private
52027     onRender : function(ct, position){
52028         if(this.el){ // from markup
52029             this.el = Roo.get(this.el);
52030         }else {  // generate
52031             var cfg = this.getAutoCreate();
52032             this.el = ct.createChild(cfg, position);
52033         }
52034         if(this.style){
52035             this.el.applyStyles(this.style);
52036         }
52037         if(this.labelAlign){
52038             this.el.addClass('x-form-label-'+this.labelAlign);
52039         }
52040         if(this.hideLabels){
52041             this.labelStyle = "display:none";
52042             this.elementStyle = "padding-left:0;";
52043         }else{
52044             if(typeof this.labelWidth == 'number'){
52045                 this.labelStyle = "width:"+this.labelWidth+"px;";
52046                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52047             }
52048             if(this.labelAlign == 'top'){
52049                 this.labelStyle = "width:auto;";
52050                 this.elementStyle = "padding-left:0;";
52051             }
52052         }
52053         var stack = this.stack;
52054         var slen = stack.length;
52055         if(slen > 0){
52056             if(!this.fieldTpl){
52057                 var t = new Roo.Template(
52058                     '<div class="x-form-item {5}">',
52059                         '<label for="{0}" style="{2}">{1}{4}</label>',
52060                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52061                         '</div>',
52062                     '</div><div class="x-form-clear-left"></div>'
52063                 );
52064                 t.disableFormats = true;
52065                 t.compile();
52066                 Roo.form.Layout.prototype.fieldTpl = t;
52067             }
52068             for(var i = 0; i < slen; i++) {
52069                 if(stack[i].isFormField){
52070                     this.renderField(stack[i]);
52071                 }else{
52072                     this.renderComponent(stack[i]);
52073                 }
52074             }
52075         }
52076         if(this.clear){
52077             this.el.createChild({cls:'x-form-clear'});
52078         }
52079     },
52080
52081     // private
52082     renderField : function(f){
52083         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52084                f.id, //0
52085                f.fieldLabel, //1
52086                f.labelStyle||this.labelStyle||'', //2
52087                this.elementStyle||'', //3
52088                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52089                f.itemCls||this.itemCls||''  //5
52090        ], true).getPrevSibling());
52091     },
52092
52093     // private
52094     renderComponent : function(c){
52095         c.render(c.isLayout ? this.el : this.el.createChild());    
52096     },
52097     /**
52098      * Adds a object form elements (using the xtype property as the factory method.)
52099      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52100      * @param {Object} config 
52101      */
52102     addxtype : function(o)
52103     {
52104         // create the lement.
52105         o.form = this.form;
52106         var fe = Roo.factory(o, Roo.form);
52107         this.form.allItems.push(fe);
52108         this.stack.push(fe);
52109         
52110         if (fe.isFormField) {
52111             this.form.items.add(fe);
52112         }
52113          
52114         return fe;
52115     }
52116 });
52117
52118 /**
52119  * @class Roo.form.Column
52120  * @extends Roo.form.Layout
52121  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52122  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52123  * @constructor
52124  * @param {Object} config Configuration options
52125  */
52126 Roo.form.Column = function(config){
52127     Roo.form.Column.superclass.constructor.call(this, config);
52128 };
52129
52130 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52131     /**
52132      * @cfg {Number/String} width
52133      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52134      */
52135     /**
52136      * @cfg {String/Object} autoCreate
52137      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52138      */
52139
52140     // private
52141     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52142
52143     // private
52144     onRender : function(ct, position){
52145         Roo.form.Column.superclass.onRender.call(this, ct, position);
52146         if(this.width){
52147             this.el.setWidth(this.width);
52148         }
52149     }
52150 });
52151
52152
52153 /**
52154  * @class Roo.form.Row
52155  * @extends Roo.form.Layout
52156  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52157  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52158  * @constructor
52159  * @param {Object} config Configuration options
52160  */
52161
52162  
52163 Roo.form.Row = function(config){
52164     Roo.form.Row.superclass.constructor.call(this, config);
52165 };
52166  
52167 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52168       /**
52169      * @cfg {Number/String} width
52170      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52171      */
52172     /**
52173      * @cfg {Number/String} height
52174      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52175      */
52176     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52177     
52178     padWidth : 20,
52179     // private
52180     onRender : function(ct, position){
52181         //console.log('row render');
52182         if(!this.rowTpl){
52183             var t = new Roo.Template(
52184                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52185                     '<label for="{0}" style="{2}">{1}{4}</label>',
52186                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52187                     '</div>',
52188                 '</div>'
52189             );
52190             t.disableFormats = true;
52191             t.compile();
52192             Roo.form.Layout.prototype.rowTpl = t;
52193         }
52194         this.fieldTpl = this.rowTpl;
52195         
52196         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52197         var labelWidth = 100;
52198         
52199         if ((this.labelAlign != 'top')) {
52200             if (typeof this.labelWidth == 'number') {
52201                 labelWidth = this.labelWidth
52202             }
52203             this.padWidth =  20 + labelWidth;
52204             
52205         }
52206         
52207         Roo.form.Column.superclass.onRender.call(this, ct, position);
52208         if(this.width){
52209             this.el.setWidth(this.width);
52210         }
52211         if(this.height){
52212             this.el.setHeight(this.height);
52213         }
52214     },
52215     
52216     // private
52217     renderField : function(f){
52218         f.fieldEl = this.fieldTpl.append(this.el, [
52219                f.id, f.fieldLabel,
52220                f.labelStyle||this.labelStyle||'',
52221                this.elementStyle||'',
52222                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52223                f.itemCls||this.itemCls||'',
52224                f.width ? f.width + this.padWidth : 160 + this.padWidth
52225        ],true);
52226     }
52227 });
52228  
52229
52230 /**
52231  * @class Roo.form.FieldSet
52232  * @extends Roo.form.Layout
52233  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52234  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52235  * @constructor
52236  * @param {Object} config Configuration options
52237  */
52238 Roo.form.FieldSet = function(config){
52239     Roo.form.FieldSet.superclass.constructor.call(this, config);
52240 };
52241
52242 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52243     /**
52244      * @cfg {String} legend
52245      * The text to display as the legend for the FieldSet (defaults to '')
52246      */
52247     /**
52248      * @cfg {String/Object} autoCreate
52249      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52250      */
52251
52252     // private
52253     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52254
52255     // private
52256     onRender : function(ct, position){
52257         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52258         if(this.legend){
52259             this.setLegend(this.legend);
52260         }
52261     },
52262
52263     // private
52264     setLegend : function(text){
52265         if(this.rendered){
52266             this.el.child('legend').update(text);
52267         }
52268     }
52269 });/*
52270  * Based on:
52271  * Ext JS Library 1.1.1
52272  * Copyright(c) 2006-2007, Ext JS, LLC.
52273  *
52274  * Originally Released Under LGPL - original licence link has changed is not relivant.
52275  *
52276  * Fork - LGPL
52277  * <script type="text/javascript">
52278  */
52279 /**
52280  * @class Roo.form.VTypes
52281  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52282  * @static
52283  */
52284 Roo.form.VTypes = function(){
52285     // closure these in so they are only created once.
52286     var alpha = /^[a-zA-Z_]+$/;
52287     var alphanum = /^[a-zA-Z0-9_]+$/;
52288     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52289     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52290
52291     // All these messages and functions are configurable
52292     return {
52293         /**
52294          * The function used to validate email addresses
52295          * @param {String} value The email address
52296          */
52297         'email' : function(v){
52298             return email.test(v);
52299         },
52300         /**
52301          * The error text to display when the email validation function returns false
52302          * @type String
52303          */
52304         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52305         /**
52306          * The keystroke filter mask to be applied on email input
52307          * @type RegExp
52308          */
52309         'emailMask' : /[a-z0-9_\.\-@]/i,
52310
52311         /**
52312          * The function used to validate URLs
52313          * @param {String} value The URL
52314          */
52315         'url' : function(v){
52316             return url.test(v);
52317         },
52318         /**
52319          * The error text to display when the url validation function returns false
52320          * @type String
52321          */
52322         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52323         
52324         /**
52325          * The function used to validate alpha values
52326          * @param {String} value The value
52327          */
52328         'alpha' : function(v){
52329             return alpha.test(v);
52330         },
52331         /**
52332          * The error text to display when the alpha validation function returns false
52333          * @type String
52334          */
52335         'alphaText' : 'This field should only contain letters and _',
52336         /**
52337          * The keystroke filter mask to be applied on alpha input
52338          * @type RegExp
52339          */
52340         'alphaMask' : /[a-z_]/i,
52341
52342         /**
52343          * The function used to validate alphanumeric values
52344          * @param {String} value The value
52345          */
52346         'alphanum' : function(v){
52347             return alphanum.test(v);
52348         },
52349         /**
52350          * The error text to display when the alphanumeric validation function returns false
52351          * @type String
52352          */
52353         'alphanumText' : 'This field should only contain letters, numbers and _',
52354         /**
52355          * The keystroke filter mask to be applied on alphanumeric input
52356          * @type RegExp
52357          */
52358         'alphanumMask' : /[a-z0-9_]/i
52359     };
52360 }();//<script type="text/javascript">
52361
52362 /**
52363  * @class Roo.form.FCKeditor
52364  * @extends Roo.form.TextArea
52365  * Wrapper around the FCKEditor http://www.fckeditor.net
52366  * @constructor
52367  * Creates a new FCKeditor
52368  * @param {Object} config Configuration options
52369  */
52370 Roo.form.FCKeditor = function(config){
52371     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52372     this.addEvents({
52373          /**
52374          * @event editorinit
52375          * Fired when the editor is initialized - you can add extra handlers here..
52376          * @param {FCKeditor} this
52377          * @param {Object} the FCK object.
52378          */
52379         editorinit : true
52380     });
52381     
52382     
52383 };
52384 Roo.form.FCKeditor.editors = { };
52385 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52386 {
52387     //defaultAutoCreate : {
52388     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52389     //},
52390     // private
52391     /**
52392      * @cfg {Object} fck options - see fck manual for details.
52393      */
52394     fckconfig : false,
52395     
52396     /**
52397      * @cfg {Object} fck toolbar set (Basic or Default)
52398      */
52399     toolbarSet : 'Basic',
52400     /**
52401      * @cfg {Object} fck BasePath
52402      */ 
52403     basePath : '/fckeditor/',
52404     
52405     
52406     frame : false,
52407     
52408     value : '',
52409     
52410    
52411     onRender : function(ct, position)
52412     {
52413         if(!this.el){
52414             this.defaultAutoCreate = {
52415                 tag: "textarea",
52416                 style:"width:300px;height:60px;",
52417                 autocomplete: "new-password"
52418             };
52419         }
52420         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52421         /*
52422         if(this.grow){
52423             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52424             if(this.preventScrollbars){
52425                 this.el.setStyle("overflow", "hidden");
52426             }
52427             this.el.setHeight(this.growMin);
52428         }
52429         */
52430         //console.log('onrender' + this.getId() );
52431         Roo.form.FCKeditor.editors[this.getId()] = this;
52432          
52433
52434         this.replaceTextarea() ;
52435         
52436     },
52437     
52438     getEditor : function() {
52439         return this.fckEditor;
52440     },
52441     /**
52442      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52443      * @param {Mixed} value The value to set
52444      */
52445     
52446     
52447     setValue : function(value)
52448     {
52449         //console.log('setValue: ' + value);
52450         
52451         if(typeof(value) == 'undefined') { // not sure why this is happending...
52452             return;
52453         }
52454         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52455         
52456         //if(!this.el || !this.getEditor()) {
52457         //    this.value = value;
52458             //this.setValue.defer(100,this,[value]);    
52459         //    return;
52460         //} 
52461         
52462         if(!this.getEditor()) {
52463             return;
52464         }
52465         
52466         this.getEditor().SetData(value);
52467         
52468         //
52469
52470     },
52471
52472     /**
52473      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52474      * @return {Mixed} value The field value
52475      */
52476     getValue : function()
52477     {
52478         
52479         if (this.frame && this.frame.dom.style.display == 'none') {
52480             return Roo.form.FCKeditor.superclass.getValue.call(this);
52481         }
52482         
52483         if(!this.el || !this.getEditor()) {
52484            
52485            // this.getValue.defer(100,this); 
52486             return this.value;
52487         }
52488        
52489         
52490         var value=this.getEditor().GetData();
52491         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52492         return Roo.form.FCKeditor.superclass.getValue.call(this);
52493         
52494
52495     },
52496
52497     /**
52498      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52499      * @return {Mixed} value The field value
52500      */
52501     getRawValue : function()
52502     {
52503         if (this.frame && this.frame.dom.style.display == 'none') {
52504             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52505         }
52506         
52507         if(!this.el || !this.getEditor()) {
52508             //this.getRawValue.defer(100,this); 
52509             return this.value;
52510             return;
52511         }
52512         
52513         
52514         
52515         var value=this.getEditor().GetData();
52516         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52517         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52518          
52519     },
52520     
52521     setSize : function(w,h) {
52522         
52523         
52524         
52525         //if (this.frame && this.frame.dom.style.display == 'none') {
52526         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52527         //    return;
52528         //}
52529         //if(!this.el || !this.getEditor()) {
52530         //    this.setSize.defer(100,this, [w,h]); 
52531         //    return;
52532         //}
52533         
52534         
52535         
52536         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52537         
52538         this.frame.dom.setAttribute('width', w);
52539         this.frame.dom.setAttribute('height', h);
52540         this.frame.setSize(w,h);
52541         
52542     },
52543     
52544     toggleSourceEdit : function(value) {
52545         
52546       
52547          
52548         this.el.dom.style.display = value ? '' : 'none';
52549         this.frame.dom.style.display = value ?  'none' : '';
52550         
52551     },
52552     
52553     
52554     focus: function(tag)
52555     {
52556         if (this.frame.dom.style.display == 'none') {
52557             return Roo.form.FCKeditor.superclass.focus.call(this);
52558         }
52559         if(!this.el || !this.getEditor()) {
52560             this.focus.defer(100,this, [tag]); 
52561             return;
52562         }
52563         
52564         
52565         
52566         
52567         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52568         this.getEditor().Focus();
52569         if (tgs.length) {
52570             if (!this.getEditor().Selection.GetSelection()) {
52571                 this.focus.defer(100,this, [tag]); 
52572                 return;
52573             }
52574             
52575             
52576             var r = this.getEditor().EditorDocument.createRange();
52577             r.setStart(tgs[0],0);
52578             r.setEnd(tgs[0],0);
52579             this.getEditor().Selection.GetSelection().removeAllRanges();
52580             this.getEditor().Selection.GetSelection().addRange(r);
52581             this.getEditor().Focus();
52582         }
52583         
52584     },
52585     
52586     
52587     
52588     replaceTextarea : function()
52589     {
52590         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52591             return ;
52592         }
52593         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52594         //{
52595             // We must check the elements firstly using the Id and then the name.
52596         var oTextarea = document.getElementById( this.getId() );
52597         
52598         var colElementsByName = document.getElementsByName( this.getId() ) ;
52599          
52600         oTextarea.style.display = 'none' ;
52601
52602         if ( oTextarea.tabIndex ) {            
52603             this.TabIndex = oTextarea.tabIndex ;
52604         }
52605         
52606         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52607         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52608         this.frame = Roo.get(this.getId() + '___Frame')
52609     },
52610     
52611     _getConfigHtml : function()
52612     {
52613         var sConfig = '' ;
52614
52615         for ( var o in this.fckconfig ) {
52616             sConfig += sConfig.length > 0  ? '&amp;' : '';
52617             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52618         }
52619
52620         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52621     },
52622     
52623     
52624     _getIFrameHtml : function()
52625     {
52626         var sFile = 'fckeditor.html' ;
52627         /* no idea what this is about..
52628         try
52629         {
52630             if ( (/fcksource=true/i).test( window.top.location.search ) )
52631                 sFile = 'fckeditor.original.html' ;
52632         }
52633         catch (e) { 
52634         */
52635
52636         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52637         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52638         
52639         
52640         var html = '<iframe id="' + this.getId() +
52641             '___Frame" src="' + sLink +
52642             '" width="' + this.width +
52643             '" height="' + this.height + '"' +
52644             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52645             ' frameborder="0" scrolling="no"></iframe>' ;
52646
52647         return html ;
52648     },
52649     
52650     _insertHtmlBefore : function( html, element )
52651     {
52652         if ( element.insertAdjacentHTML )       {
52653             // IE
52654             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52655         } else { // Gecko
52656             var oRange = document.createRange() ;
52657             oRange.setStartBefore( element ) ;
52658             var oFragment = oRange.createContextualFragment( html );
52659             element.parentNode.insertBefore( oFragment, element ) ;
52660         }
52661     }
52662     
52663     
52664   
52665     
52666     
52667     
52668     
52669
52670 });
52671
52672 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52673
52674 function FCKeditor_OnComplete(editorInstance){
52675     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52676     f.fckEditor = editorInstance;
52677     //console.log("loaded");
52678     f.fireEvent('editorinit', f, editorInstance);
52679
52680   
52681
52682  
52683
52684
52685
52686
52687
52688
52689
52690
52691
52692
52693
52694
52695
52696
52697
52698 //<script type="text/javascript">
52699 /**
52700  * @class Roo.form.GridField
52701  * @extends Roo.form.Field
52702  * Embed a grid (or editable grid into a form)
52703  * STATUS ALPHA
52704  * 
52705  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52706  * it needs 
52707  * xgrid.store = Roo.data.Store
52708  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52709  * xgrid.store.reader = Roo.data.JsonReader 
52710  * 
52711  * 
52712  * @constructor
52713  * Creates a new GridField
52714  * @param {Object} config Configuration options
52715  */
52716 Roo.form.GridField = function(config){
52717     Roo.form.GridField.superclass.constructor.call(this, config);
52718      
52719 };
52720
52721 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52722     /**
52723      * @cfg {Number} width  - used to restrict width of grid..
52724      */
52725     width : 100,
52726     /**
52727      * @cfg {Number} height - used to restrict height of grid..
52728      */
52729     height : 50,
52730      /**
52731      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52732          * 
52733          *}
52734      */
52735     xgrid : false, 
52736     /**
52737      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52738      * {tag: "input", type: "checkbox", autocomplete: "off"})
52739      */
52740    // defaultAutoCreate : { tag: 'div' },
52741     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52742     /**
52743      * @cfg {String} addTitle Text to include for adding a title.
52744      */
52745     addTitle : false,
52746     //
52747     onResize : function(){
52748         Roo.form.Field.superclass.onResize.apply(this, arguments);
52749     },
52750
52751     initEvents : function(){
52752         // Roo.form.Checkbox.superclass.initEvents.call(this);
52753         // has no events...
52754        
52755     },
52756
52757
52758     getResizeEl : function(){
52759         return this.wrap;
52760     },
52761
52762     getPositionEl : function(){
52763         return this.wrap;
52764     },
52765
52766     // private
52767     onRender : function(ct, position){
52768         
52769         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52770         var style = this.style;
52771         delete this.style;
52772         
52773         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52774         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52775         this.viewEl = this.wrap.createChild({ tag: 'div' });
52776         if (style) {
52777             this.viewEl.applyStyles(style);
52778         }
52779         if (this.width) {
52780             this.viewEl.setWidth(this.width);
52781         }
52782         if (this.height) {
52783             this.viewEl.setHeight(this.height);
52784         }
52785         //if(this.inputValue !== undefined){
52786         //this.setValue(this.value);
52787         
52788         
52789         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52790         
52791         
52792         this.grid.render();
52793         this.grid.getDataSource().on('remove', this.refreshValue, this);
52794         this.grid.getDataSource().on('update', this.refreshValue, this);
52795         this.grid.on('afteredit', this.refreshValue, this);
52796  
52797     },
52798      
52799     
52800     /**
52801      * Sets the value of the item. 
52802      * @param {String} either an object  or a string..
52803      */
52804     setValue : function(v){
52805         //this.value = v;
52806         v = v || []; // empty set..
52807         // this does not seem smart - it really only affects memoryproxy grids..
52808         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52809             var ds = this.grid.getDataSource();
52810             // assumes a json reader..
52811             var data = {}
52812             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52813             ds.loadData( data);
52814         }
52815         // clear selection so it does not get stale.
52816         if (this.grid.sm) { 
52817             this.grid.sm.clearSelections();
52818         }
52819         
52820         Roo.form.GridField.superclass.setValue.call(this, v);
52821         this.refreshValue();
52822         // should load data in the grid really....
52823     },
52824     
52825     // private
52826     refreshValue: function() {
52827          var val = [];
52828         this.grid.getDataSource().each(function(r) {
52829             val.push(r.data);
52830         });
52831         this.el.dom.value = Roo.encode(val);
52832     }
52833     
52834      
52835     
52836     
52837 });/*
52838  * Based on:
52839  * Ext JS Library 1.1.1
52840  * Copyright(c) 2006-2007, Ext JS, LLC.
52841  *
52842  * Originally Released Under LGPL - original licence link has changed is not relivant.
52843  *
52844  * Fork - LGPL
52845  * <script type="text/javascript">
52846  */
52847 /**
52848  * @class Roo.form.DisplayField
52849  * @extends Roo.form.Field
52850  * A generic Field to display non-editable data.
52851  * @cfg {Boolean} closable (true|false) default false
52852  * @constructor
52853  * Creates a new Display Field item.
52854  * @param {Object} config Configuration options
52855  */
52856 Roo.form.DisplayField = function(config){
52857     Roo.form.DisplayField.superclass.constructor.call(this, config);
52858     
52859     this.addEvents({
52860         /**
52861          * @event close
52862          * Fires after the click the close btn
52863              * @param {Roo.form.DisplayField} this
52864              */
52865         close : true
52866     });
52867 };
52868
52869 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52870     inputType:      'hidden',
52871     allowBlank:     true,
52872     readOnly:         true,
52873     
52874  
52875     /**
52876      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52877      */
52878     focusClass : undefined,
52879     /**
52880      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52881      */
52882     fieldClass: 'x-form-field',
52883     
52884      /**
52885      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52886      */
52887     valueRenderer: undefined,
52888     
52889     width: 100,
52890     /**
52891      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52892      * {tag: "input", type: "checkbox", autocomplete: "off"})
52893      */
52894      
52895  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52896  
52897     closable : false,
52898     
52899     onResize : function(){
52900         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52901         
52902     },
52903
52904     initEvents : function(){
52905         // Roo.form.Checkbox.superclass.initEvents.call(this);
52906         // has no events...
52907         
52908         if(this.closable){
52909             this.closeEl.on('click', this.onClose, this);
52910         }
52911        
52912     },
52913
52914
52915     getResizeEl : function(){
52916         return this.wrap;
52917     },
52918
52919     getPositionEl : function(){
52920         return this.wrap;
52921     },
52922
52923     // private
52924     onRender : function(ct, position){
52925         
52926         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52927         //if(this.inputValue !== undefined){
52928         this.wrap = this.el.wrap();
52929         
52930         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52931         
52932         if(this.closable){
52933             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52934         }
52935         
52936         if (this.bodyStyle) {
52937             this.viewEl.applyStyles(this.bodyStyle);
52938         }
52939         //this.viewEl.setStyle('padding', '2px');
52940         
52941         this.setValue(this.value);
52942         
52943     },
52944 /*
52945     // private
52946     initValue : Roo.emptyFn,
52947
52948   */
52949
52950         // private
52951     onClick : function(){
52952         
52953     },
52954
52955     /**
52956      * Sets the checked state of the checkbox.
52957      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52958      */
52959     setValue : function(v){
52960         this.value = v;
52961         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52962         // this might be called before we have a dom element..
52963         if (!this.viewEl) {
52964             return;
52965         }
52966         this.viewEl.dom.innerHTML = html;
52967         Roo.form.DisplayField.superclass.setValue.call(this, v);
52968
52969     },
52970     
52971     onClose : function(e)
52972     {
52973         e.preventDefault();
52974         
52975         this.fireEvent('close', this);
52976     }
52977 });/*
52978  * 
52979  * Licence- LGPL
52980  * 
52981  */
52982
52983 /**
52984  * @class Roo.form.DayPicker
52985  * @extends Roo.form.Field
52986  * A Day picker show [M] [T] [W] ....
52987  * @constructor
52988  * Creates a new Day Picker
52989  * @param {Object} config Configuration options
52990  */
52991 Roo.form.DayPicker= function(config){
52992     Roo.form.DayPicker.superclass.constructor.call(this, config);
52993      
52994 };
52995
52996 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52997     /**
52998      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52999      */
53000     focusClass : undefined,
53001     /**
53002      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
53003      */
53004     fieldClass: "x-form-field",
53005    
53006     /**
53007      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
53008      * {tag: "input", type: "checkbox", autocomplete: "off"})
53009      */
53010     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
53011     
53012    
53013     actionMode : 'viewEl', 
53014     //
53015     // private
53016  
53017     inputType : 'hidden',
53018     
53019      
53020     inputElement: false, // real input element?
53021     basedOn: false, // ????
53022     
53023     isFormField: true, // not sure where this is needed!!!!
53024
53025     onResize : function(){
53026         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
53027         if(!this.boxLabel){
53028             this.el.alignTo(this.wrap, 'c-c');
53029         }
53030     },
53031
53032     initEvents : function(){
53033         Roo.form.Checkbox.superclass.initEvents.call(this);
53034         this.el.on("click", this.onClick,  this);
53035         this.el.on("change", this.onClick,  this);
53036     },
53037
53038
53039     getResizeEl : function(){
53040         return this.wrap;
53041     },
53042
53043     getPositionEl : function(){
53044         return this.wrap;
53045     },
53046
53047     
53048     // private
53049     onRender : function(ct, position){
53050         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53051        
53052         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53053         
53054         var r1 = '<table><tr>';
53055         var r2 = '<tr class="x-form-daypick-icons">';
53056         for (var i=0; i < 7; i++) {
53057             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53058             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53059         }
53060         
53061         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53062         viewEl.select('img').on('click', this.onClick, this);
53063         this.viewEl = viewEl;   
53064         
53065         
53066         // this will not work on Chrome!!!
53067         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53068         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53069         
53070         
53071           
53072
53073     },
53074
53075     // private
53076     initValue : Roo.emptyFn,
53077
53078     /**
53079      * Returns the checked state of the checkbox.
53080      * @return {Boolean} True if checked, else false
53081      */
53082     getValue : function(){
53083         return this.el.dom.value;
53084         
53085     },
53086
53087         // private
53088     onClick : function(e){ 
53089         //this.setChecked(!this.checked);
53090         Roo.get(e.target).toggleClass('x-menu-item-checked');
53091         this.refreshValue();
53092         //if(this.el.dom.checked != this.checked){
53093         //    this.setValue(this.el.dom.checked);
53094        // }
53095     },
53096     
53097     // private
53098     refreshValue : function()
53099     {
53100         var val = '';
53101         this.viewEl.select('img',true).each(function(e,i,n)  {
53102             val += e.is(".x-menu-item-checked") ? String(n) : '';
53103         });
53104         this.setValue(val, true);
53105     },
53106
53107     /**
53108      * Sets the checked state of the checkbox.
53109      * On is always based on a string comparison between inputValue and the param.
53110      * @param {Boolean/String} value - the value to set 
53111      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53112      */
53113     setValue : function(v,suppressEvent){
53114         if (!this.el.dom) {
53115             return;
53116         }
53117         var old = this.el.dom.value ;
53118         this.el.dom.value = v;
53119         if (suppressEvent) {
53120             return ;
53121         }
53122          
53123         // update display..
53124         this.viewEl.select('img',true).each(function(e,i,n)  {
53125             
53126             var on = e.is(".x-menu-item-checked");
53127             var newv = v.indexOf(String(n)) > -1;
53128             if (on != newv) {
53129                 e.toggleClass('x-menu-item-checked');
53130             }
53131             
53132         });
53133         
53134         
53135         this.fireEvent('change', this, v, old);
53136         
53137         
53138     },
53139    
53140     // handle setting of hidden value by some other method!!?!?
53141     setFromHidden: function()
53142     {
53143         if(!this.el){
53144             return;
53145         }
53146         //console.log("SET FROM HIDDEN");
53147         //alert('setFrom hidden');
53148         this.setValue(this.el.dom.value);
53149     },
53150     
53151     onDestroy : function()
53152     {
53153         if(this.viewEl){
53154             Roo.get(this.viewEl).remove();
53155         }
53156          
53157         Roo.form.DayPicker.superclass.onDestroy.call(this);
53158     }
53159
53160 });/*
53161  * RooJS Library 1.1.1
53162  * Copyright(c) 2008-2011  Alan Knowles
53163  *
53164  * License - LGPL
53165  */
53166  
53167
53168 /**
53169  * @class Roo.form.ComboCheck
53170  * @extends Roo.form.ComboBox
53171  * A combobox for multiple select items.
53172  *
53173  * FIXME - could do with a reset button..
53174  * 
53175  * @constructor
53176  * Create a new ComboCheck
53177  * @param {Object} config Configuration options
53178  */
53179 Roo.form.ComboCheck = function(config){
53180     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53181     // should verify some data...
53182     // like
53183     // hiddenName = required..
53184     // displayField = required
53185     // valudField == required
53186     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53187     var _t = this;
53188     Roo.each(req, function(e) {
53189         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53190             throw "Roo.form.ComboCheck : missing value for: " + e;
53191         }
53192     });
53193     
53194     
53195 };
53196
53197 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53198      
53199      
53200     editable : false,
53201      
53202     selectedClass: 'x-menu-item-checked', 
53203     
53204     // private
53205     onRender : function(ct, position){
53206         var _t = this;
53207         
53208         
53209         
53210         if(!this.tpl){
53211             var cls = 'x-combo-list';
53212
53213             
53214             this.tpl =  new Roo.Template({
53215                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53216                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53217                    '<span>{' + this.displayField + '}</span>' +
53218                     '</div>' 
53219                 
53220             });
53221         }
53222  
53223         
53224         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53225         this.view.singleSelect = false;
53226         this.view.multiSelect = true;
53227         this.view.toggleSelect = true;
53228         this.pageTb.add(new Roo.Toolbar.Fill(), {
53229             
53230             text: 'Done',
53231             handler: function()
53232             {
53233                 _t.collapse();
53234             }
53235         });
53236     },
53237     
53238     onViewOver : function(e, t){
53239         // do nothing...
53240         return;
53241         
53242     },
53243     
53244     onViewClick : function(doFocus,index){
53245         return;
53246         
53247     },
53248     select: function () {
53249         //Roo.log("SELECT CALLED");
53250     },
53251      
53252     selectByValue : function(xv, scrollIntoView){
53253         var ar = this.getValueArray();
53254         var sels = [];
53255         
53256         Roo.each(ar, function(v) {
53257             if(v === undefined || v === null){
53258                 return;
53259             }
53260             var r = this.findRecord(this.valueField, v);
53261             if(r){
53262                 sels.push(this.store.indexOf(r))
53263                 
53264             }
53265         },this);
53266         this.view.select(sels);
53267         return false;
53268     },
53269     
53270     
53271     
53272     onSelect : function(record, index){
53273        // Roo.log("onselect Called");
53274        // this is only called by the clear button now..
53275         this.view.clearSelections();
53276         this.setValue('[]');
53277         if (this.value != this.valueBefore) {
53278             this.fireEvent('change', this, this.value, this.valueBefore);
53279             this.valueBefore = this.value;
53280         }
53281     },
53282     getValueArray : function()
53283     {
53284         var ar = [] ;
53285         
53286         try {
53287             //Roo.log(this.value);
53288             if (typeof(this.value) == 'undefined') {
53289                 return [];
53290             }
53291             var ar = Roo.decode(this.value);
53292             return  ar instanceof Array ? ar : []; //?? valid?
53293             
53294         } catch(e) {
53295             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53296             return [];
53297         }
53298          
53299     },
53300     expand : function ()
53301     {
53302         
53303         Roo.form.ComboCheck.superclass.expand.call(this);
53304         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53305         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53306         
53307
53308     },
53309     
53310     collapse : function(){
53311         Roo.form.ComboCheck.superclass.collapse.call(this);
53312         var sl = this.view.getSelectedIndexes();
53313         var st = this.store;
53314         var nv = [];
53315         var tv = [];
53316         var r;
53317         Roo.each(sl, function(i) {
53318             r = st.getAt(i);
53319             nv.push(r.get(this.valueField));
53320         },this);
53321         this.setValue(Roo.encode(nv));
53322         if (this.value != this.valueBefore) {
53323
53324             this.fireEvent('change', this, this.value, this.valueBefore);
53325             this.valueBefore = this.value;
53326         }
53327         
53328     },
53329     
53330     setValue : function(v){
53331         // Roo.log(v);
53332         this.value = v;
53333         
53334         var vals = this.getValueArray();
53335         var tv = [];
53336         Roo.each(vals, function(k) {
53337             var r = this.findRecord(this.valueField, k);
53338             if(r){
53339                 tv.push(r.data[this.displayField]);
53340             }else if(this.valueNotFoundText !== undefined){
53341                 tv.push( this.valueNotFoundText );
53342             }
53343         },this);
53344        // Roo.log(tv);
53345         
53346         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53347         this.hiddenField.value = v;
53348         this.value = v;
53349     }
53350     
53351 });/*
53352  * Based on:
53353  * Ext JS Library 1.1.1
53354  * Copyright(c) 2006-2007, Ext JS, LLC.
53355  *
53356  * Originally Released Under LGPL - original licence link has changed is not relivant.
53357  *
53358  * Fork - LGPL
53359  * <script type="text/javascript">
53360  */
53361  
53362 /**
53363  * @class Roo.form.Signature
53364  * @extends Roo.form.Field
53365  * Signature field.  
53366  * @constructor
53367  * 
53368  * @param {Object} config Configuration options
53369  */
53370
53371 Roo.form.Signature = function(config){
53372     Roo.form.Signature.superclass.constructor.call(this, config);
53373     
53374     this.addEvents({// not in used??
53375          /**
53376          * @event confirm
53377          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53378              * @param {Roo.form.Signature} combo This combo box
53379              */
53380         'confirm' : true,
53381         /**
53382          * @event reset
53383          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53384              * @param {Roo.form.ComboBox} combo This combo box
53385              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53386              */
53387         'reset' : true
53388     });
53389 };
53390
53391 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53392     /**
53393      * @cfg {Object} labels Label to use when rendering a form.
53394      * defaults to 
53395      * labels : { 
53396      *      clear : "Clear",
53397      *      confirm : "Confirm"
53398      *  }
53399      */
53400     labels : { 
53401         clear : "Clear",
53402         confirm : "Confirm"
53403     },
53404     /**
53405      * @cfg {Number} width The signature panel width (defaults to 300)
53406      */
53407     width: 300,
53408     /**
53409      * @cfg {Number} height The signature panel height (defaults to 100)
53410      */
53411     height : 100,
53412     /**
53413      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53414      */
53415     allowBlank : false,
53416     
53417     //private
53418     // {Object} signPanel The signature SVG panel element (defaults to {})
53419     signPanel : {},
53420     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53421     isMouseDown : false,
53422     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53423     isConfirmed : false,
53424     // {String} signatureTmp SVG mapping string (defaults to empty string)
53425     signatureTmp : '',
53426     
53427     
53428     defaultAutoCreate : { // modified by initCompnoent..
53429         tag: "input",
53430         type:"hidden"
53431     },
53432
53433     // private
53434     onRender : function(ct, position){
53435         
53436         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53437         
53438         this.wrap = this.el.wrap({
53439             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53440         });
53441         
53442         this.createToolbar(this);
53443         this.signPanel = this.wrap.createChild({
53444                 tag: 'div',
53445                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53446             }, this.el
53447         );
53448             
53449         this.svgID = Roo.id();
53450         this.svgEl = this.signPanel.createChild({
53451               xmlns : 'http://www.w3.org/2000/svg',
53452               tag : 'svg',
53453               id : this.svgID + "-svg",
53454               width: this.width,
53455               height: this.height,
53456               viewBox: '0 0 '+this.width+' '+this.height,
53457               cn : [
53458                 {
53459                     tag: "rect",
53460                     id: this.svgID + "-svg-r",
53461                     width: this.width,
53462                     height: this.height,
53463                     fill: "#ffa"
53464                 },
53465                 {
53466                     tag: "line",
53467                     id: this.svgID + "-svg-l",
53468                     x1: "0", // start
53469                     y1: (this.height*0.8), // start set the line in 80% of height
53470                     x2: this.width, // end
53471                     y2: (this.height*0.8), // end set the line in 80% of height
53472                     'stroke': "#666",
53473                     'stroke-width': "1",
53474                     'stroke-dasharray': "3",
53475                     'shape-rendering': "crispEdges",
53476                     'pointer-events': "none"
53477                 },
53478                 {
53479                     tag: "path",
53480                     id: this.svgID + "-svg-p",
53481                     'stroke': "navy",
53482                     'stroke-width': "3",
53483                     'fill': "none",
53484                     'pointer-events': 'none'
53485                 }
53486               ]
53487         });
53488         this.createSVG();
53489         this.svgBox = this.svgEl.dom.getScreenCTM();
53490     },
53491     createSVG : function(){ 
53492         var svg = this.signPanel;
53493         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53494         var t = this;
53495
53496         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53497         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53498         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53499         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53500         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53501         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53502         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53503         
53504     },
53505     isTouchEvent : function(e){
53506         return e.type.match(/^touch/);
53507     },
53508     getCoords : function (e) {
53509         var pt    = this.svgEl.dom.createSVGPoint();
53510         pt.x = e.clientX; 
53511         pt.y = e.clientY;
53512         if (this.isTouchEvent(e)) {
53513             pt.x =  e.targetTouches[0].clientX;
53514             pt.y = e.targetTouches[0].clientY;
53515         }
53516         var a = this.svgEl.dom.getScreenCTM();
53517         var b = a.inverse();
53518         var mx = pt.matrixTransform(b);
53519         return mx.x + ',' + mx.y;
53520     },
53521     //mouse event headler 
53522     down : function (e) {
53523         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53524         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53525         
53526         this.isMouseDown = true;
53527         
53528         e.preventDefault();
53529     },
53530     move : function (e) {
53531         if (this.isMouseDown) {
53532             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53533             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53534         }
53535         
53536         e.preventDefault();
53537     },
53538     up : function (e) {
53539         this.isMouseDown = false;
53540         var sp = this.signatureTmp.split(' ');
53541         
53542         if(sp.length > 1){
53543             if(!sp[sp.length-2].match(/^L/)){
53544                 sp.pop();
53545                 sp.pop();
53546                 sp.push("");
53547                 this.signatureTmp = sp.join(" ");
53548             }
53549         }
53550         if(this.getValue() != this.signatureTmp){
53551             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53552             this.isConfirmed = false;
53553         }
53554         e.preventDefault();
53555     },
53556     
53557     /**
53558      * Protected method that will not generally be called directly. It
53559      * is called when the editor creates its toolbar. Override this method if you need to
53560      * add custom toolbar buttons.
53561      * @param {HtmlEditor} editor
53562      */
53563     createToolbar : function(editor){
53564          function btn(id, toggle, handler){
53565             var xid = fid + '-'+ id ;
53566             return {
53567                 id : xid,
53568                 cmd : id,
53569                 cls : 'x-btn-icon x-edit-'+id,
53570                 enableToggle:toggle !== false,
53571                 scope: editor, // was editor...
53572                 handler:handler||editor.relayBtnCmd,
53573                 clickEvent:'mousedown',
53574                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53575                 tabIndex:-1
53576             };
53577         }
53578         
53579         
53580         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53581         this.tb = tb;
53582         this.tb.add(
53583            {
53584                 cls : ' x-signature-btn x-signature-'+id,
53585                 scope: editor, // was editor...
53586                 handler: this.reset,
53587                 clickEvent:'mousedown',
53588                 text: this.labels.clear
53589             },
53590             {
53591                  xtype : 'Fill',
53592                  xns: Roo.Toolbar
53593             }, 
53594             {
53595                 cls : '  x-signature-btn x-signature-'+id,
53596                 scope: editor, // was editor...
53597                 handler: this.confirmHandler,
53598                 clickEvent:'mousedown',
53599                 text: this.labels.confirm
53600             }
53601         );
53602     
53603     },
53604     //public
53605     /**
53606      * when user is clicked confirm then show this image.....
53607      * 
53608      * @return {String} Image Data URI
53609      */
53610     getImageDataURI : function(){
53611         var svg = this.svgEl.dom.parentNode.innerHTML;
53612         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53613         return src; 
53614     },
53615     /**
53616      * 
53617      * @return {Boolean} this.isConfirmed
53618      */
53619     getConfirmed : function(){
53620         return this.isConfirmed;
53621     },
53622     /**
53623      * 
53624      * @return {Number} this.width
53625      */
53626     getWidth : function(){
53627         return this.width;
53628     },
53629     /**
53630      * 
53631      * @return {Number} this.height
53632      */
53633     getHeight : function(){
53634         return this.height;
53635     },
53636     // private
53637     getSignature : function(){
53638         return this.signatureTmp;
53639     },
53640     // private
53641     reset : function(){
53642         this.signatureTmp = '';
53643         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53644         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53645         this.isConfirmed = false;
53646         Roo.form.Signature.superclass.reset.call(this);
53647     },
53648     setSignature : function(s){
53649         this.signatureTmp = s;
53650         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53651         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53652         this.setValue(s);
53653         this.isConfirmed = false;
53654         Roo.form.Signature.superclass.reset.call(this);
53655     }, 
53656     test : function(){
53657 //        Roo.log(this.signPanel.dom.contentWindow.up())
53658     },
53659     //private
53660     setConfirmed : function(){
53661         
53662         
53663         
53664 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53665     },
53666     // private
53667     confirmHandler : function(){
53668         if(!this.getSignature()){
53669             return;
53670         }
53671         
53672         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53673         this.setValue(this.getSignature());
53674         this.isConfirmed = true;
53675         
53676         this.fireEvent('confirm', this);
53677     },
53678     // private
53679     // Subclasses should provide the validation implementation by overriding this
53680     validateValue : function(value){
53681         if(this.allowBlank){
53682             return true;
53683         }
53684         
53685         if(this.isConfirmed){
53686             return true;
53687         }
53688         return false;
53689     }
53690 });/*
53691  * Based on:
53692  * Ext JS Library 1.1.1
53693  * Copyright(c) 2006-2007, Ext JS, LLC.
53694  *
53695  * Originally Released Under LGPL - original licence link has changed is not relivant.
53696  *
53697  * Fork - LGPL
53698  * <script type="text/javascript">
53699  */
53700  
53701
53702 /**
53703  * @class Roo.form.ComboBox
53704  * @extends Roo.form.TriggerField
53705  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53706  * @constructor
53707  * Create a new ComboBox.
53708  * @param {Object} config Configuration options
53709  */
53710 Roo.form.Select = function(config){
53711     Roo.form.Select.superclass.constructor.call(this, config);
53712      
53713 };
53714
53715 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53716     /**
53717      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53718      */
53719     /**
53720      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53721      * rendering into an Roo.Editor, defaults to false)
53722      */
53723     /**
53724      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53725      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53726      */
53727     /**
53728      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53729      */
53730     /**
53731      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53732      * the dropdown list (defaults to undefined, with no header element)
53733      */
53734
53735      /**
53736      * @cfg {String/Roo.Template} tpl The template to use to render the output
53737      */
53738      
53739     // private
53740     defaultAutoCreate : {tag: "select"  },
53741     /**
53742      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53743      */
53744     listWidth: undefined,
53745     /**
53746      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53747      * mode = 'remote' or 'text' if mode = 'local')
53748      */
53749     displayField: undefined,
53750     /**
53751      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53752      * mode = 'remote' or 'value' if mode = 'local'). 
53753      * Note: use of a valueField requires the user make a selection
53754      * in order for a value to be mapped.
53755      */
53756     valueField: undefined,
53757     
53758     
53759     /**
53760      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53761      * field's data value (defaults to the underlying DOM element's name)
53762      */
53763     hiddenName: undefined,
53764     /**
53765      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53766      */
53767     listClass: '',
53768     /**
53769      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53770      */
53771     selectedClass: 'x-combo-selected',
53772     /**
53773      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53774      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53775      * which displays a downward arrow icon).
53776      */
53777     triggerClass : 'x-form-arrow-trigger',
53778     /**
53779      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53780      */
53781     shadow:'sides',
53782     /**
53783      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53784      * anchor positions (defaults to 'tl-bl')
53785      */
53786     listAlign: 'tl-bl?',
53787     /**
53788      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53789      */
53790     maxHeight: 300,
53791     /**
53792      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53793      * query specified by the allQuery config option (defaults to 'query')
53794      */
53795     triggerAction: 'query',
53796     /**
53797      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53798      * (defaults to 4, does not apply if editable = false)
53799      */
53800     minChars : 4,
53801     /**
53802      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53803      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53804      */
53805     typeAhead: false,
53806     /**
53807      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53808      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53809      */
53810     queryDelay: 500,
53811     /**
53812      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53813      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53814      */
53815     pageSize: 0,
53816     /**
53817      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53818      * when editable = true (defaults to false)
53819      */
53820     selectOnFocus:false,
53821     /**
53822      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53823      */
53824     queryParam: 'query',
53825     /**
53826      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53827      * when mode = 'remote' (defaults to 'Loading...')
53828      */
53829     loadingText: 'Loading...',
53830     /**
53831      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53832      */
53833     resizable: false,
53834     /**
53835      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53836      */
53837     handleHeight : 8,
53838     /**
53839      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53840      * traditional select (defaults to true)
53841      */
53842     editable: true,
53843     /**
53844      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53845      */
53846     allQuery: '',
53847     /**
53848      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53849      */
53850     mode: 'remote',
53851     /**
53852      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53853      * listWidth has a higher value)
53854      */
53855     minListWidth : 70,
53856     /**
53857      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53858      * allow the user to set arbitrary text into the field (defaults to false)
53859      */
53860     forceSelection:false,
53861     /**
53862      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53863      * if typeAhead = true (defaults to 250)
53864      */
53865     typeAheadDelay : 250,
53866     /**
53867      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53868      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53869      */
53870     valueNotFoundText : undefined,
53871     
53872     /**
53873      * @cfg {String} defaultValue The value displayed after loading the store.
53874      */
53875     defaultValue: '',
53876     
53877     /**
53878      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53879      */
53880     blockFocus : false,
53881     
53882     /**
53883      * @cfg {Boolean} disableClear Disable showing of clear button.
53884      */
53885     disableClear : false,
53886     /**
53887      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53888      */
53889     alwaysQuery : false,
53890     
53891     //private
53892     addicon : false,
53893     editicon: false,
53894     
53895     // element that contains real text value.. (when hidden is used..)
53896      
53897     // private
53898     onRender : function(ct, position){
53899         Roo.form.Field.prototype.onRender.call(this, ct, position);
53900         
53901         if(this.store){
53902             this.store.on('beforeload', this.onBeforeLoad, this);
53903             this.store.on('load', this.onLoad, this);
53904             this.store.on('loadexception', this.onLoadException, this);
53905             this.store.load({});
53906         }
53907         
53908         
53909         
53910     },
53911
53912     // private
53913     initEvents : function(){
53914         //Roo.form.ComboBox.superclass.initEvents.call(this);
53915  
53916     },
53917
53918     onDestroy : function(){
53919        
53920         if(this.store){
53921             this.store.un('beforeload', this.onBeforeLoad, this);
53922             this.store.un('load', this.onLoad, this);
53923             this.store.un('loadexception', this.onLoadException, this);
53924         }
53925         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53926     },
53927
53928     // private
53929     fireKey : function(e){
53930         if(e.isNavKeyPress() && !this.list.isVisible()){
53931             this.fireEvent("specialkey", this, e);
53932         }
53933     },
53934
53935     // private
53936     onResize: function(w, h){
53937         
53938         return; 
53939     
53940         
53941     },
53942
53943     /**
53944      * Allow or prevent the user from directly editing the field text.  If false is passed,
53945      * the user will only be able to select from the items defined in the dropdown list.  This method
53946      * is the runtime equivalent of setting the 'editable' config option at config time.
53947      * @param {Boolean} value True to allow the user to directly edit the field text
53948      */
53949     setEditable : function(value){
53950          
53951     },
53952
53953     // private
53954     onBeforeLoad : function(){
53955         
53956         Roo.log("Select before load");
53957         return;
53958     
53959         this.innerList.update(this.loadingText ?
53960                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53961         //this.restrictHeight();
53962         this.selectedIndex = -1;
53963     },
53964
53965     // private
53966     onLoad : function(){
53967
53968     
53969         var dom = this.el.dom;
53970         dom.innerHTML = '';
53971          var od = dom.ownerDocument;
53972          
53973         if (this.emptyText) {
53974             var op = od.createElement('option');
53975             op.setAttribute('value', '');
53976             op.innerHTML = String.format('{0}', this.emptyText);
53977             dom.appendChild(op);
53978         }
53979         if(this.store.getCount() > 0){
53980            
53981             var vf = this.valueField;
53982             var df = this.displayField;
53983             this.store.data.each(function(r) {
53984                 // which colmsn to use... testing - cdoe / title..
53985                 var op = od.createElement('option');
53986                 op.setAttribute('value', r.data[vf]);
53987                 op.innerHTML = String.format('{0}', r.data[df]);
53988                 dom.appendChild(op);
53989             });
53990             if (typeof(this.defaultValue != 'undefined')) {
53991                 this.setValue(this.defaultValue);
53992             }
53993             
53994              
53995         }else{
53996             //this.onEmptyResults();
53997         }
53998         //this.el.focus();
53999     },
54000     // private
54001     onLoadException : function()
54002     {
54003         dom.innerHTML = '';
54004             
54005         Roo.log("Select on load exception");
54006         return;
54007     
54008         this.collapse();
54009         Roo.log(this.store.reader.jsonData);
54010         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
54011             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
54012         }
54013         
54014         
54015     },
54016     // private
54017     onTypeAhead : function(){
54018          
54019     },
54020
54021     // private
54022     onSelect : function(record, index){
54023         Roo.log('on select?');
54024         return;
54025         if(this.fireEvent('beforeselect', this, record, index) !== false){
54026             this.setFromData(index > -1 ? record.data : false);
54027             this.collapse();
54028             this.fireEvent('select', this, record, index);
54029         }
54030     },
54031
54032     /**
54033      * Returns the currently selected field value or empty string if no value is set.
54034      * @return {String} value The selected value
54035      */
54036     getValue : function(){
54037         var dom = this.el.dom;
54038         this.value = dom.options[dom.selectedIndex].value;
54039         return this.value;
54040         
54041     },
54042
54043     /**
54044      * Clears any text/value currently set in the field
54045      */
54046     clearValue : function(){
54047         this.value = '';
54048         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54049         
54050     },
54051
54052     /**
54053      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54054      * will be displayed in the field.  If the value does not match the data value of an existing item,
54055      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54056      * Otherwise the field will be blank (although the value will still be set).
54057      * @param {String} value The value to match
54058      */
54059     setValue : function(v){
54060         var d = this.el.dom;
54061         for (var i =0; i < d.options.length;i++) {
54062             if (v == d.options[i].value) {
54063                 d.selectedIndex = i;
54064                 this.value = v;
54065                 return;
54066             }
54067         }
54068         this.clearValue();
54069     },
54070     /**
54071      * @property {Object} the last set data for the element
54072      */
54073     
54074     lastData : false,
54075     /**
54076      * Sets the value of the field based on a object which is related to the record format for the store.
54077      * @param {Object} value the value to set as. or false on reset?
54078      */
54079     setFromData : function(o){
54080         Roo.log('setfrom data?');
54081          
54082         
54083         
54084     },
54085     // private
54086     reset : function(){
54087         this.clearValue();
54088     },
54089     // private
54090     findRecord : function(prop, value){
54091         
54092         return false;
54093     
54094         var record;
54095         if(this.store.getCount() > 0){
54096             this.store.each(function(r){
54097                 if(r.data[prop] == value){
54098                     record = r;
54099                     return false;
54100                 }
54101                 return true;
54102             });
54103         }
54104         return record;
54105     },
54106     
54107     getName: function()
54108     {
54109         // returns hidden if it's set..
54110         if (!this.rendered) {return ''};
54111         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54112         
54113     },
54114      
54115
54116     
54117
54118     // private
54119     onEmptyResults : function(){
54120         Roo.log('empty results');
54121         //this.collapse();
54122     },
54123
54124     /**
54125      * Returns true if the dropdown list is expanded, else false.
54126      */
54127     isExpanded : function(){
54128         return false;
54129     },
54130
54131     /**
54132      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54133      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54134      * @param {String} value The data value of the item to select
54135      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54136      * selected item if it is not currently in view (defaults to true)
54137      * @return {Boolean} True if the value matched an item in the list, else false
54138      */
54139     selectByValue : function(v, scrollIntoView){
54140         Roo.log('select By Value');
54141         return false;
54142     
54143         if(v !== undefined && v !== null){
54144             var r = this.findRecord(this.valueField || this.displayField, v);
54145             if(r){
54146                 this.select(this.store.indexOf(r), scrollIntoView);
54147                 return true;
54148             }
54149         }
54150         return false;
54151     },
54152
54153     /**
54154      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54156      * @param {Number} index The zero-based index of the list item to select
54157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54158      * selected item if it is not currently in view (defaults to true)
54159      */
54160     select : function(index, scrollIntoView){
54161         Roo.log('select ');
54162         return  ;
54163         
54164         this.selectedIndex = index;
54165         this.view.select(index);
54166         if(scrollIntoView !== false){
54167             var el = this.view.getNode(index);
54168             if(el){
54169                 this.innerList.scrollChildIntoView(el, false);
54170             }
54171         }
54172     },
54173
54174       
54175
54176     // private
54177     validateBlur : function(){
54178         
54179         return;
54180         
54181     },
54182
54183     // private
54184     initQuery : function(){
54185         this.doQuery(this.getRawValue());
54186     },
54187
54188     // private
54189     doForce : function(){
54190         if(this.el.dom.value.length > 0){
54191             this.el.dom.value =
54192                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54193              
54194         }
54195     },
54196
54197     /**
54198      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54199      * query allowing the query action to be canceled if needed.
54200      * @param {String} query The SQL query to execute
54201      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54202      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54203      * saved in the current store (defaults to false)
54204      */
54205     doQuery : function(q, forceAll){
54206         
54207         Roo.log('doQuery?');
54208         if(q === undefined || q === null){
54209             q = '';
54210         }
54211         var qe = {
54212             query: q,
54213             forceAll: forceAll,
54214             combo: this,
54215             cancel:false
54216         };
54217         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54218             return false;
54219         }
54220         q = qe.query;
54221         forceAll = qe.forceAll;
54222         if(forceAll === true || (q.length >= this.minChars)){
54223             if(this.lastQuery != q || this.alwaysQuery){
54224                 this.lastQuery = q;
54225                 if(this.mode == 'local'){
54226                     this.selectedIndex = -1;
54227                     if(forceAll){
54228                         this.store.clearFilter();
54229                     }else{
54230                         this.store.filter(this.displayField, q);
54231                     }
54232                     this.onLoad();
54233                 }else{
54234                     this.store.baseParams[this.queryParam] = q;
54235                     this.store.load({
54236                         params: this.getParams(q)
54237                     });
54238                     this.expand();
54239                 }
54240             }else{
54241                 this.selectedIndex = -1;
54242                 this.onLoad();   
54243             }
54244         }
54245     },
54246
54247     // private
54248     getParams : function(q){
54249         var p = {};
54250         //p[this.queryParam] = q;
54251         if(this.pageSize){
54252             p.start = 0;
54253             p.limit = this.pageSize;
54254         }
54255         return p;
54256     },
54257
54258     /**
54259      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54260      */
54261     collapse : function(){
54262         
54263     },
54264
54265     // private
54266     collapseIf : function(e){
54267         
54268     },
54269
54270     /**
54271      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54272      */
54273     expand : function(){
54274         
54275     } ,
54276
54277     // private
54278      
54279
54280     /** 
54281     * @cfg {Boolean} grow 
54282     * @hide 
54283     */
54284     /** 
54285     * @cfg {Number} growMin 
54286     * @hide 
54287     */
54288     /** 
54289     * @cfg {Number} growMax 
54290     * @hide 
54291     */
54292     /**
54293      * @hide
54294      * @method autoSize
54295      */
54296     
54297     setWidth : function()
54298     {
54299         
54300     },
54301     getResizeEl : function(){
54302         return this.el;
54303     }
54304 });//<script type="text/javasscript">
54305  
54306
54307 /**
54308  * @class Roo.DDView
54309  * A DnD enabled version of Roo.View.
54310  * @param {Element/String} container The Element in which to create the View.
54311  * @param {String} tpl The template string used to create the markup for each element of the View
54312  * @param {Object} config The configuration properties. These include all the config options of
54313  * {@link Roo.View} plus some specific to this class.<br>
54314  * <p>
54315  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54316  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54317  * <p>
54318  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54319 .x-view-drag-insert-above {
54320         border-top:1px dotted #3366cc;
54321 }
54322 .x-view-drag-insert-below {
54323         border-bottom:1px dotted #3366cc;
54324 }
54325 </code></pre>
54326  * 
54327  */
54328  
54329 Roo.DDView = function(container, tpl, config) {
54330     Roo.DDView.superclass.constructor.apply(this, arguments);
54331     this.getEl().setStyle("outline", "0px none");
54332     this.getEl().unselectable();
54333     if (this.dragGroup) {
54334         this.setDraggable(this.dragGroup.split(","));
54335     }
54336     if (this.dropGroup) {
54337         this.setDroppable(this.dropGroup.split(","));
54338     }
54339     if (this.deletable) {
54340         this.setDeletable();
54341     }
54342     this.isDirtyFlag = false;
54343         this.addEvents({
54344                 "drop" : true
54345         });
54346 };
54347
54348 Roo.extend(Roo.DDView, Roo.View, {
54349 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54350 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54351 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54352 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54353
54354         isFormField: true,
54355
54356         reset: Roo.emptyFn,
54357         
54358         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54359
54360         validate: function() {
54361                 return true;
54362         },
54363         
54364         destroy: function() {
54365                 this.purgeListeners();
54366                 this.getEl.removeAllListeners();
54367                 this.getEl().remove();
54368                 if (this.dragZone) {
54369                         if (this.dragZone.destroy) {
54370                                 this.dragZone.destroy();
54371                         }
54372                 }
54373                 if (this.dropZone) {
54374                         if (this.dropZone.destroy) {
54375                                 this.dropZone.destroy();
54376                         }
54377                 }
54378         },
54379
54380 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54381         getName: function() {
54382                 return this.name;
54383         },
54384
54385 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54386         setValue: function(v) {
54387                 if (!this.store) {
54388                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54389                 }
54390                 var data = {};
54391                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54392                 this.store.proxy = new Roo.data.MemoryProxy(data);
54393                 this.store.load();
54394         },
54395
54396 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54397         getValue: function() {
54398                 var result = '(';
54399                 this.store.each(function(rec) {
54400                         result += rec.id + ',';
54401                 });
54402                 return result.substr(0, result.length - 1) + ')';
54403         },
54404         
54405         getIds: function() {
54406                 var i = 0, result = new Array(this.store.getCount());
54407                 this.store.each(function(rec) {
54408                         result[i++] = rec.id;
54409                 });
54410                 return result;
54411         },
54412         
54413         isDirty: function() {
54414                 return this.isDirtyFlag;
54415         },
54416
54417 /**
54418  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54419  *      whole Element becomes the target, and this causes the drop gesture to append.
54420  */
54421     getTargetFromEvent : function(e) {
54422                 var target = e.getTarget();
54423                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54424                 target = target.parentNode;
54425                 }
54426                 if (!target) {
54427                         target = this.el.dom.lastChild || this.el.dom;
54428                 }
54429                 return target;
54430     },
54431
54432 /**
54433  *      Create the drag data which consists of an object which has the property "ddel" as
54434  *      the drag proxy element. 
54435  */
54436     getDragData : function(e) {
54437         var target = this.findItemFromChild(e.getTarget());
54438                 if(target) {
54439                         this.handleSelection(e);
54440                         var selNodes = this.getSelectedNodes();
54441             var dragData = {
54442                 source: this,
54443                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54444                 nodes: selNodes,
54445                 records: []
54446                         };
54447                         var selectedIndices = this.getSelectedIndexes();
54448                         for (var i = 0; i < selectedIndices.length; i++) {
54449                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54450                         }
54451                         if (selNodes.length == 1) {
54452                                 dragData.ddel = target.cloneNode(true); // the div element
54453                         } else {
54454                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54455                                 div.className = 'multi-proxy';
54456                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54457                                         div.appendChild(selNodes[i].cloneNode(true));
54458                                 }
54459                                 dragData.ddel = div;
54460                         }
54461             //console.log(dragData)
54462             //console.log(dragData.ddel.innerHTML)
54463                         return dragData;
54464                 }
54465         //console.log('nodragData')
54466                 return false;
54467     },
54468     
54469 /**     Specify to which ddGroup items in this DDView may be dragged. */
54470     setDraggable: function(ddGroup) {
54471         if (ddGroup instanceof Array) {
54472                 Roo.each(ddGroup, this.setDraggable, this);
54473                 return;
54474         }
54475         if (this.dragZone) {
54476                 this.dragZone.addToGroup(ddGroup);
54477         } else {
54478                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54479                                 containerScroll: true,
54480                                 ddGroup: ddGroup 
54481
54482                         });
54483 //                      Draggability implies selection. DragZone's mousedown selects the element.
54484                         if (!this.multiSelect) { this.singleSelect = true; }
54485
54486 //                      Wire the DragZone's handlers up to methods in *this*
54487                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54488                 }
54489     },
54490
54491 /**     Specify from which ddGroup this DDView accepts drops. */
54492     setDroppable: function(ddGroup) {
54493         if (ddGroup instanceof Array) {
54494                 Roo.each(ddGroup, this.setDroppable, this);
54495                 return;
54496         }
54497         if (this.dropZone) {
54498                 this.dropZone.addToGroup(ddGroup);
54499         } else {
54500                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54501                                 containerScroll: true,
54502                                 ddGroup: ddGroup
54503                         });
54504
54505 //                      Wire the DropZone's handlers up to methods in *this*
54506                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54507                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54508                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54509                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54510                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54511                 }
54512     },
54513
54514 /**     Decide whether to drop above or below a View node. */
54515     getDropPoint : function(e, n, dd){
54516         if (n == this.el.dom) { return "above"; }
54517                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54518                 var c = t + (b - t) / 2;
54519                 var y = Roo.lib.Event.getPageY(e);
54520                 if(y <= c) {
54521                         return "above";
54522                 }else{
54523                         return "below";
54524                 }
54525     },
54526
54527     onNodeEnter : function(n, dd, e, data){
54528                 return false;
54529     },
54530     
54531     onNodeOver : function(n, dd, e, data){
54532                 var pt = this.getDropPoint(e, n, dd);
54533                 // set the insert point style on the target node
54534                 var dragElClass = this.dropNotAllowed;
54535                 if (pt) {
54536                         var targetElClass;
54537                         if (pt == "above"){
54538                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54539                                 targetElClass = "x-view-drag-insert-above";
54540                         } else {
54541                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54542                                 targetElClass = "x-view-drag-insert-below";
54543                         }
54544                         if (this.lastInsertClass != targetElClass){
54545                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54546                                 this.lastInsertClass = targetElClass;
54547                         }
54548                 }
54549                 return dragElClass;
54550         },
54551
54552     onNodeOut : function(n, dd, e, data){
54553                 this.removeDropIndicators(n);
54554     },
54555
54556     onNodeDrop : function(n, dd, e, data){
54557         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54558                 return false;
54559         }
54560         var pt = this.getDropPoint(e, n, dd);
54561                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54562                 if (pt == "below") { insertAt++; }
54563                 for (var i = 0; i < data.records.length; i++) {
54564                         var r = data.records[i];
54565                         var dup = this.store.getById(r.id);
54566                         if (dup && (dd != this.dragZone)) {
54567                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54568                         } else {
54569                                 if (data.copy) {
54570                                         this.store.insert(insertAt++, r.copy());
54571                                 } else {
54572                                         data.source.isDirtyFlag = true;
54573                                         r.store.remove(r);
54574                                         this.store.insert(insertAt++, r);
54575                                 }
54576                                 this.isDirtyFlag = true;
54577                         }
54578                 }
54579                 this.dragZone.cachedTarget = null;
54580                 return true;
54581     },
54582
54583     removeDropIndicators : function(n){
54584                 if(n){
54585                         Roo.fly(n).removeClass([
54586                                 "x-view-drag-insert-above",
54587                                 "x-view-drag-insert-below"]);
54588                         this.lastInsertClass = "_noclass";
54589                 }
54590     },
54591
54592 /**
54593  *      Utility method. Add a delete option to the DDView's context menu.
54594  *      @param {String} imageUrl The URL of the "delete" icon image.
54595  */
54596         setDeletable: function(imageUrl) {
54597                 if (!this.singleSelect && !this.multiSelect) {
54598                         this.singleSelect = true;
54599                 }
54600                 var c = this.getContextMenu();
54601                 this.contextMenu.on("itemclick", function(item) {
54602                         switch (item.id) {
54603                                 case "delete":
54604                                         this.remove(this.getSelectedIndexes());
54605                                         break;
54606                         }
54607                 }, this);
54608                 this.contextMenu.add({
54609                         icon: imageUrl,
54610                         id: "delete",
54611                         text: 'Delete'
54612                 });
54613         },
54614         
54615 /**     Return the context menu for this DDView. */
54616         getContextMenu: function() {
54617                 if (!this.contextMenu) {
54618 //                      Create the View's context menu
54619                         this.contextMenu = new Roo.menu.Menu({
54620                                 id: this.id + "-contextmenu"
54621                         });
54622                         this.el.on("contextmenu", this.showContextMenu, this);
54623                 }
54624                 return this.contextMenu;
54625         },
54626         
54627         disableContextMenu: function() {
54628                 if (this.contextMenu) {
54629                         this.el.un("contextmenu", this.showContextMenu, this);
54630                 }
54631         },
54632
54633         showContextMenu: function(e, item) {
54634         item = this.findItemFromChild(e.getTarget());
54635                 if (item) {
54636                         e.stopEvent();
54637                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54638                         this.contextMenu.showAt(e.getXY());
54639             }
54640     },
54641
54642 /**
54643  *      Remove {@link Roo.data.Record}s at the specified indices.
54644  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54645  */
54646     remove: function(selectedIndices) {
54647                 selectedIndices = [].concat(selectedIndices);
54648                 for (var i = 0; i < selectedIndices.length; i++) {
54649                         var rec = this.store.getAt(selectedIndices[i]);
54650                         this.store.remove(rec);
54651                 }
54652     },
54653
54654 /**
54655  *      Double click fires the event, but also, if this is draggable, and there is only one other
54656  *      related DropZone, it transfers the selected node.
54657  */
54658     onDblClick : function(e){
54659         var item = this.findItemFromChild(e.getTarget());
54660         if(item){
54661             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54662                 return false;
54663             }
54664             if (this.dragGroup) {
54665                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54666                     while (targets.indexOf(this.dropZone) > -1) {
54667                             targets.remove(this.dropZone);
54668                                 }
54669                     if (targets.length == 1) {
54670                                         this.dragZone.cachedTarget = null;
54671                         var el = Roo.get(targets[0].getEl());
54672                         var box = el.getBox(true);
54673                         targets[0].onNodeDrop(el.dom, {
54674                                 target: el.dom,
54675                                 xy: [box.x, box.y + box.height - 1]
54676                         }, null, this.getDragData(e));
54677                     }
54678                 }
54679         }
54680     },
54681     
54682     handleSelection: function(e) {
54683                 this.dragZone.cachedTarget = null;
54684         var item = this.findItemFromChild(e.getTarget());
54685         if (!item) {
54686                 this.clearSelections(true);
54687                 return;
54688         }
54689                 if (item && (this.multiSelect || this.singleSelect)){
54690                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54691                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54692                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54693                                 this.unselect(item);
54694                         } else {
54695                                 this.select(item, this.multiSelect && e.ctrlKey);
54696                                 this.lastSelection = item;
54697                         }
54698                 }
54699     },
54700
54701     onItemClick : function(item, index, e){
54702                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54703                         return false;
54704                 }
54705                 return true;
54706     },
54707
54708     unselect : function(nodeInfo, suppressEvent){
54709                 var node = this.getNode(nodeInfo);
54710                 if(node && this.isSelected(node)){
54711                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54712                                 Roo.fly(node).removeClass(this.selectedClass);
54713                                 this.selections.remove(node);
54714                                 if(!suppressEvent){
54715                                         this.fireEvent("selectionchange", this, this.selections);
54716                                 }
54717                         }
54718                 }
54719     }
54720 });
54721 /*
54722  * Based on:
54723  * Ext JS Library 1.1.1
54724  * Copyright(c) 2006-2007, Ext JS, LLC.
54725  *
54726  * Originally Released Under LGPL - original licence link has changed is not relivant.
54727  *
54728  * Fork - LGPL
54729  * <script type="text/javascript">
54730  */
54731  
54732 /**
54733  * @class Roo.LayoutManager
54734  * @extends Roo.util.Observable
54735  * Base class for layout managers.
54736  */
54737 Roo.LayoutManager = function(container, config){
54738     Roo.LayoutManager.superclass.constructor.call(this);
54739     this.el = Roo.get(container);
54740     // ie scrollbar fix
54741     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54742         document.body.scroll = "no";
54743     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54744         this.el.position('relative');
54745     }
54746     this.id = this.el.id;
54747     this.el.addClass("x-layout-container");
54748     /** false to disable window resize monitoring @type Boolean */
54749     this.monitorWindowResize = true;
54750     this.regions = {};
54751     this.addEvents({
54752         /**
54753          * @event layout
54754          * Fires when a layout is performed. 
54755          * @param {Roo.LayoutManager} this
54756          */
54757         "layout" : true,
54758         /**
54759          * @event regionresized
54760          * Fires when the user resizes a region. 
54761          * @param {Roo.LayoutRegion} region The resized region
54762          * @param {Number} newSize The new size (width for east/west, height for north/south)
54763          */
54764         "regionresized" : true,
54765         /**
54766          * @event regioncollapsed
54767          * Fires when a region is collapsed. 
54768          * @param {Roo.LayoutRegion} region The collapsed region
54769          */
54770         "regioncollapsed" : true,
54771         /**
54772          * @event regionexpanded
54773          * Fires when a region is expanded.  
54774          * @param {Roo.LayoutRegion} region The expanded region
54775          */
54776         "regionexpanded" : true
54777     });
54778     this.updating = false;
54779     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54780 };
54781
54782 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54783     /**
54784      * Returns true if this layout is currently being updated
54785      * @return {Boolean}
54786      */
54787     isUpdating : function(){
54788         return this.updating; 
54789     },
54790     
54791     /**
54792      * Suspend the LayoutManager from doing auto-layouts while
54793      * making multiple add or remove calls
54794      */
54795     beginUpdate : function(){
54796         this.updating = true;    
54797     },
54798     
54799     /**
54800      * Restore auto-layouts and optionally disable the manager from performing a layout
54801      * @param {Boolean} noLayout true to disable a layout update 
54802      */
54803     endUpdate : function(noLayout){
54804         this.updating = false;
54805         if(!noLayout){
54806             this.layout();
54807         }    
54808     },
54809     
54810     layout: function(){
54811         
54812     },
54813     
54814     onRegionResized : function(region, newSize){
54815         this.fireEvent("regionresized", region, newSize);
54816         this.layout();
54817     },
54818     
54819     onRegionCollapsed : function(region){
54820         this.fireEvent("regioncollapsed", region);
54821     },
54822     
54823     onRegionExpanded : function(region){
54824         this.fireEvent("regionexpanded", region);
54825     },
54826         
54827     /**
54828      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54829      * performs box-model adjustments.
54830      * @return {Object} The size as an object {width: (the width), height: (the height)}
54831      */
54832     getViewSize : function(){
54833         var size;
54834         if(this.el.dom != document.body){
54835             size = this.el.getSize();
54836         }else{
54837             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54838         }
54839         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54840         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54841         return size;
54842     },
54843     
54844     /**
54845      * Returns the Element this layout is bound to.
54846      * @return {Roo.Element}
54847      */
54848     getEl : function(){
54849         return this.el;
54850     },
54851     
54852     /**
54853      * Returns the specified region.
54854      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54855      * @return {Roo.LayoutRegion}
54856      */
54857     getRegion : function(target){
54858         return this.regions[target.toLowerCase()];
54859     },
54860     
54861     onWindowResize : function(){
54862         if(this.monitorWindowResize){
54863             this.layout();
54864         }
54865     }
54866 });/*
54867  * Based on:
54868  * Ext JS Library 1.1.1
54869  * Copyright(c) 2006-2007, Ext JS, LLC.
54870  *
54871  * Originally Released Under LGPL - original licence link has changed is not relivant.
54872  *
54873  * Fork - LGPL
54874  * <script type="text/javascript">
54875  */
54876 /**
54877  * @class Roo.BorderLayout
54878  * @extends Roo.LayoutManager
54879  * @children Roo.ContentPanel
54880  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54881  * please see: <br><br>
54882  * <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>
54883  * <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>
54884  * Example:
54885  <pre><code>
54886  var layout = new Roo.BorderLayout(document.body, {
54887     north: {
54888         initialSize: 25,
54889         titlebar: false
54890     },
54891     west: {
54892         split:true,
54893         initialSize: 200,
54894         minSize: 175,
54895         maxSize: 400,
54896         titlebar: true,
54897         collapsible: true
54898     },
54899     east: {
54900         split:true,
54901         initialSize: 202,
54902         minSize: 175,
54903         maxSize: 400,
54904         titlebar: true,
54905         collapsible: true
54906     },
54907     south: {
54908         split:true,
54909         initialSize: 100,
54910         minSize: 100,
54911         maxSize: 200,
54912         titlebar: true,
54913         collapsible: true
54914     },
54915     center: {
54916         titlebar: true,
54917         autoScroll:true,
54918         resizeTabs: true,
54919         minTabWidth: 50,
54920         preferredTabWidth: 150
54921     }
54922 });
54923
54924 // shorthand
54925 var CP = Roo.ContentPanel;
54926
54927 layout.beginUpdate();
54928 layout.add("north", new CP("north", "North"));
54929 layout.add("south", new CP("south", {title: "South", closable: true}));
54930 layout.add("west", new CP("west", {title: "West"}));
54931 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54932 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54933 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54934 layout.getRegion("center").showPanel("center1");
54935 layout.endUpdate();
54936 </code></pre>
54937
54938 <b>The container the layout is rendered into can be either the body element or any other element.
54939 If it is not the body element, the container needs to either be an absolute positioned element,
54940 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54941 the container size if it is not the body element.</b>
54942
54943 * @constructor
54944 * Create a new BorderLayout
54945 * @param {String/HTMLElement/Element} container The container this layout is bound to
54946 * @param {Object} config Configuration options
54947  */
54948 Roo.BorderLayout = function(container, config){
54949     config = config || {};
54950     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54951     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54952     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54953         var target = this.factory.validRegions[i];
54954         if(config[target]){
54955             this.addRegion(target, config[target]);
54956         }
54957     }
54958 };
54959
54960 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54961         
54962         /**
54963          * @cfg {Roo.LayoutRegion} east
54964          */
54965         /**
54966          * @cfg {Roo.LayoutRegion} west
54967          */
54968         /**
54969          * @cfg {Roo.LayoutRegion} north
54970          */
54971         /**
54972          * @cfg {Roo.LayoutRegion} south
54973          */
54974         /**
54975          * @cfg {Roo.LayoutRegion} center
54976          */
54977     /**
54978      * Creates and adds a new region if it doesn't already exist.
54979      * @param {String} target The target region key (north, south, east, west or center).
54980      * @param {Object} config The regions config object
54981      * @return {BorderLayoutRegion} The new region
54982      */
54983     addRegion : function(target, config){
54984         if(!this.regions[target]){
54985             var r = this.factory.create(target, this, config);
54986             this.bindRegion(target, r);
54987         }
54988         return this.regions[target];
54989     },
54990
54991     // private (kinda)
54992     bindRegion : function(name, r){
54993         this.regions[name] = r;
54994         r.on("visibilitychange", this.layout, this);
54995         r.on("paneladded", this.layout, this);
54996         r.on("panelremoved", this.layout, this);
54997         r.on("invalidated", this.layout, this);
54998         r.on("resized", this.onRegionResized, this);
54999         r.on("collapsed", this.onRegionCollapsed, this);
55000         r.on("expanded", this.onRegionExpanded, this);
55001     },
55002
55003     /**
55004      * Performs a layout update.
55005      */
55006     layout : function(){
55007         if(this.updating) {
55008             return;
55009         }
55010         var size = this.getViewSize();
55011         var w = size.width;
55012         var h = size.height;
55013         var centerW = w;
55014         var centerH = h;
55015         var centerY = 0;
55016         var centerX = 0;
55017         //var x = 0, y = 0;
55018
55019         var rs = this.regions;
55020         var north = rs["north"];
55021         var south = rs["south"]; 
55022         var west = rs["west"];
55023         var east = rs["east"];
55024         var center = rs["center"];
55025         //if(this.hideOnLayout){ // not supported anymore
55026             //c.el.setStyle("display", "none");
55027         //}
55028         if(north && north.isVisible()){
55029             var b = north.getBox();
55030             var m = north.getMargins();
55031             b.width = w - (m.left+m.right);
55032             b.x = m.left;
55033             b.y = m.top;
55034             centerY = b.height + b.y + m.bottom;
55035             centerH -= centerY;
55036             north.updateBox(this.safeBox(b));
55037         }
55038         if(south && south.isVisible()){
55039             var b = south.getBox();
55040             var m = south.getMargins();
55041             b.width = w - (m.left+m.right);
55042             b.x = m.left;
55043             var totalHeight = (b.height + m.top + m.bottom);
55044             b.y = h - totalHeight + m.top;
55045             centerH -= totalHeight;
55046             south.updateBox(this.safeBox(b));
55047         }
55048         if(west && west.isVisible()){
55049             var b = west.getBox();
55050             var m = west.getMargins();
55051             b.height = centerH - (m.top+m.bottom);
55052             b.x = m.left;
55053             b.y = centerY + m.top;
55054             var totalWidth = (b.width + m.left + m.right);
55055             centerX += totalWidth;
55056             centerW -= totalWidth;
55057             west.updateBox(this.safeBox(b));
55058         }
55059         if(east && east.isVisible()){
55060             var b = east.getBox();
55061             var m = east.getMargins();
55062             b.height = centerH - (m.top+m.bottom);
55063             var totalWidth = (b.width + m.left + m.right);
55064             b.x = w - totalWidth + m.left;
55065             b.y = centerY + m.top;
55066             centerW -= totalWidth;
55067             east.updateBox(this.safeBox(b));
55068         }
55069         if(center){
55070             var m = center.getMargins();
55071             var centerBox = {
55072                 x: centerX + m.left,
55073                 y: centerY + m.top,
55074                 width: centerW - (m.left+m.right),
55075                 height: centerH - (m.top+m.bottom)
55076             };
55077             //if(this.hideOnLayout){
55078                 //center.el.setStyle("display", "block");
55079             //}
55080             center.updateBox(this.safeBox(centerBox));
55081         }
55082         this.el.repaint();
55083         this.fireEvent("layout", this);
55084     },
55085
55086     // private
55087     safeBox : function(box){
55088         box.width = Math.max(0, box.width);
55089         box.height = Math.max(0, box.height);
55090         return box;
55091     },
55092
55093     /**
55094      * Adds a ContentPanel (or subclass) to this layout.
55095      * @param {String} target The target region key (north, south, east, west or center).
55096      * @param {Roo.ContentPanel} panel The panel to add
55097      * @return {Roo.ContentPanel} The added panel
55098      */
55099     add : function(target, panel){
55100          
55101         target = target.toLowerCase();
55102         return this.regions[target].add(panel);
55103     },
55104
55105     /**
55106      * Remove a ContentPanel (or subclass) to this layout.
55107      * @param {String} target The target region key (north, south, east, west or center).
55108      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55109      * @return {Roo.ContentPanel} The removed panel
55110      */
55111     remove : function(target, panel){
55112         target = target.toLowerCase();
55113         return this.regions[target].remove(panel);
55114     },
55115
55116     /**
55117      * Searches all regions for a panel with the specified id
55118      * @param {String} panelId
55119      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55120      */
55121     findPanel : function(panelId){
55122         var rs = this.regions;
55123         for(var target in rs){
55124             if(typeof rs[target] != "function"){
55125                 var p = rs[target].getPanel(panelId);
55126                 if(p){
55127                     return p;
55128                 }
55129             }
55130         }
55131         return null;
55132     },
55133
55134     /**
55135      * Searches all regions for a panel with the specified id and activates (shows) it.
55136      * @param {String/ContentPanel} panelId The panels id or the panel itself
55137      * @return {Roo.ContentPanel} The shown panel or null
55138      */
55139     showPanel : function(panelId) {
55140       var rs = this.regions;
55141       for(var target in rs){
55142          var r = rs[target];
55143          if(typeof r != "function"){
55144             if(r.hasPanel(panelId)){
55145                return r.showPanel(panelId);
55146             }
55147          }
55148       }
55149       return null;
55150    },
55151
55152    /**
55153      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55154      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55155      */
55156     restoreState : function(provider){
55157         if(!provider){
55158             provider = Roo.state.Manager;
55159         }
55160         var sm = new Roo.LayoutStateManager();
55161         sm.init(this, provider);
55162     },
55163
55164     /**
55165      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55166      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55167      * a valid ContentPanel config object.  Example:
55168      * <pre><code>
55169 // Create the main layout
55170 var layout = new Roo.BorderLayout('main-ct', {
55171     west: {
55172         split:true,
55173         minSize: 175,
55174         titlebar: true
55175     },
55176     center: {
55177         title:'Components'
55178     }
55179 }, 'main-ct');
55180
55181 // Create and add multiple ContentPanels at once via configs
55182 layout.batchAdd({
55183    west: {
55184        id: 'source-files',
55185        autoCreate:true,
55186        title:'Ext Source Files',
55187        autoScroll:true,
55188        fitToFrame:true
55189    },
55190    center : {
55191        el: cview,
55192        autoScroll:true,
55193        fitToFrame:true,
55194        toolbar: tb,
55195        resizeEl:'cbody'
55196    }
55197 });
55198 </code></pre>
55199      * @param {Object} regions An object containing ContentPanel configs by region name
55200      */
55201     batchAdd : function(regions){
55202         this.beginUpdate();
55203         for(var rname in regions){
55204             var lr = this.regions[rname];
55205             if(lr){
55206                 this.addTypedPanels(lr, regions[rname]);
55207             }
55208         }
55209         this.endUpdate();
55210     },
55211
55212     // private
55213     addTypedPanels : function(lr, ps){
55214         if(typeof ps == 'string'){
55215             lr.add(new Roo.ContentPanel(ps));
55216         }
55217         else if(ps instanceof Array){
55218             for(var i =0, len = ps.length; i < len; i++){
55219                 this.addTypedPanels(lr, ps[i]);
55220             }
55221         }
55222         else if(!ps.events){ // raw config?
55223             var el = ps.el;
55224             delete ps.el; // prevent conflict
55225             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55226         }
55227         else {  // panel object assumed!
55228             lr.add(ps);
55229         }
55230     },
55231     /**
55232      * Adds a xtype elements to the layout.
55233      * <pre><code>
55234
55235 layout.addxtype({
55236        xtype : 'ContentPanel',
55237        region: 'west',
55238        items: [ .... ]
55239    }
55240 );
55241
55242 layout.addxtype({
55243         xtype : 'NestedLayoutPanel',
55244         region: 'west',
55245         layout: {
55246            center: { },
55247            west: { }   
55248         },
55249         items : [ ... list of content panels or nested layout panels.. ]
55250    }
55251 );
55252 </code></pre>
55253      * @param {Object} cfg Xtype definition of item to add.
55254      */
55255     addxtype : function(cfg)
55256     {
55257         // basically accepts a pannel...
55258         // can accept a layout region..!?!?
55259         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55260         
55261         if (!cfg.xtype.match(/Panel$/)) {
55262             return false;
55263         }
55264         var ret = false;
55265         
55266         if (typeof(cfg.region) == 'undefined') {
55267             Roo.log("Failed to add Panel, region was not set");
55268             Roo.log(cfg);
55269             return false;
55270         }
55271         var region = cfg.region;
55272         delete cfg.region;
55273         
55274           
55275         var xitems = [];
55276         if (cfg.items) {
55277             xitems = cfg.items;
55278             delete cfg.items;
55279         }
55280         var nb = false;
55281         
55282         switch(cfg.xtype) 
55283         {
55284             case 'ContentPanel':  // ContentPanel (el, cfg)
55285             case 'ScrollPanel':  // ContentPanel (el, cfg)
55286             case 'ViewPanel': 
55287                 if(cfg.autoCreate) {
55288                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55289                 } else {
55290                     var el = this.el.createChild();
55291                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55292                 }
55293                 
55294                 this.add(region, ret);
55295                 break;
55296             
55297             
55298             case 'TreePanel': // our new panel!
55299                 cfg.el = this.el.createChild();
55300                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55301                 this.add(region, ret);
55302                 break;
55303             
55304             case 'NestedLayoutPanel': 
55305                 // create a new Layout (which is  a Border Layout...
55306                 var el = this.el.createChild();
55307                 var clayout = cfg.layout;
55308                 delete cfg.layout;
55309                 clayout.items   = clayout.items  || [];
55310                 // replace this exitems with the clayout ones..
55311                 xitems = clayout.items;
55312                  
55313                 
55314                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55315                     cfg.background = false;
55316                 }
55317                 var layout = new Roo.BorderLayout(el, clayout);
55318                 
55319                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55320                 //console.log('adding nested layout panel '  + cfg.toSource());
55321                 this.add(region, ret);
55322                 nb = {}; /// find first...
55323                 break;
55324                 
55325             case 'GridPanel': 
55326             
55327                 // needs grid and region
55328                 
55329                 //var el = this.getRegion(region).el.createChild();
55330                 var el = this.el.createChild();
55331                 // create the grid first...
55332                 
55333                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55334                 delete cfg.grid;
55335                 if (region == 'center' && this.active ) {
55336                     cfg.background = false;
55337                 }
55338                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55339                 
55340                 this.add(region, ret);
55341                 if (cfg.background) {
55342                     ret.on('activate', function(gp) {
55343                         if (!gp.grid.rendered) {
55344                             gp.grid.render();
55345                         }
55346                     });
55347                 } else {
55348                     grid.render();
55349                 }
55350                 break;
55351            
55352            
55353            
55354                 
55355                 
55356                 
55357             default:
55358                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55359                     
55360                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55361                     this.add(region, ret);
55362                 } else {
55363                 
55364                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55365                     return null;
55366                 }
55367                 
55368              // GridPanel (grid, cfg)
55369             
55370         }
55371         this.beginUpdate();
55372         // add children..
55373         var region = '';
55374         var abn = {};
55375         Roo.each(xitems, function(i)  {
55376             region = nb && i.region ? i.region : false;
55377             
55378             var add = ret.addxtype(i);
55379            
55380             if (region) {
55381                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55382                 if (!i.background) {
55383                     abn[region] = nb[region] ;
55384                 }
55385             }
55386             
55387         });
55388         this.endUpdate();
55389
55390         // make the last non-background panel active..
55391         //if (nb) { Roo.log(abn); }
55392         if (nb) {
55393             
55394             for(var r in abn) {
55395                 region = this.getRegion(r);
55396                 if (region) {
55397                     // tried using nb[r], but it does not work..
55398                      
55399                     region.showPanel(abn[r]);
55400                    
55401                 }
55402             }
55403         }
55404         return ret;
55405         
55406     }
55407 });
55408
55409 /**
55410  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55411  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55412  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55413  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55414  * <pre><code>
55415 // shorthand
55416 var CP = Roo.ContentPanel;
55417
55418 var layout = Roo.BorderLayout.create({
55419     north: {
55420         initialSize: 25,
55421         titlebar: false,
55422         panels: [new CP("north", "North")]
55423     },
55424     west: {
55425         split:true,
55426         initialSize: 200,
55427         minSize: 175,
55428         maxSize: 400,
55429         titlebar: true,
55430         collapsible: true,
55431         panels: [new CP("west", {title: "West"})]
55432     },
55433     east: {
55434         split:true,
55435         initialSize: 202,
55436         minSize: 175,
55437         maxSize: 400,
55438         titlebar: true,
55439         collapsible: true,
55440         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55441     },
55442     south: {
55443         split:true,
55444         initialSize: 100,
55445         minSize: 100,
55446         maxSize: 200,
55447         titlebar: true,
55448         collapsible: true,
55449         panels: [new CP("south", {title: "South", closable: true})]
55450     },
55451     center: {
55452         titlebar: true,
55453         autoScroll:true,
55454         resizeTabs: true,
55455         minTabWidth: 50,
55456         preferredTabWidth: 150,
55457         panels: [
55458             new CP("center1", {title: "Close Me", closable: true}),
55459             new CP("center2", {title: "Center Panel", closable: false})
55460         ]
55461     }
55462 }, document.body);
55463
55464 layout.getRegion("center").showPanel("center1");
55465 </code></pre>
55466  * @param config
55467  * @param targetEl
55468  */
55469 Roo.BorderLayout.create = function(config, targetEl){
55470     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55471     layout.beginUpdate();
55472     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55473     for(var j = 0, jlen = regions.length; j < jlen; j++){
55474         var lr = regions[j];
55475         if(layout.regions[lr] && config[lr].panels){
55476             var r = layout.regions[lr];
55477             var ps = config[lr].panels;
55478             layout.addTypedPanels(r, ps);
55479         }
55480     }
55481     layout.endUpdate();
55482     return layout;
55483 };
55484
55485 // private
55486 Roo.BorderLayout.RegionFactory = {
55487     // private
55488     validRegions : ["north","south","east","west","center"],
55489
55490     // private
55491     create : function(target, mgr, config){
55492         target = target.toLowerCase();
55493         if(config.lightweight || config.basic){
55494             return new Roo.BasicLayoutRegion(mgr, config, target);
55495         }
55496         switch(target){
55497             case "north":
55498                 return new Roo.NorthLayoutRegion(mgr, config);
55499             case "south":
55500                 return new Roo.SouthLayoutRegion(mgr, config);
55501             case "east":
55502                 return new Roo.EastLayoutRegion(mgr, config);
55503             case "west":
55504                 return new Roo.WestLayoutRegion(mgr, config);
55505             case "center":
55506                 return new Roo.CenterLayoutRegion(mgr, config);
55507         }
55508         throw 'Layout region "'+target+'" not supported.';
55509     }
55510 };/*
55511  * Based on:
55512  * Ext JS Library 1.1.1
55513  * Copyright(c) 2006-2007, Ext JS, LLC.
55514  *
55515  * Originally Released Under LGPL - original licence link has changed is not relivant.
55516  *
55517  * Fork - LGPL
55518  * <script type="text/javascript">
55519  */
55520  
55521 /**
55522  * @class Roo.BasicLayoutRegion
55523  * @extends Roo.util.Observable
55524  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55525  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55526  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55527  */
55528 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55529     this.mgr = mgr;
55530     this.position  = pos;
55531     this.events = {
55532         /**
55533          * @scope Roo.BasicLayoutRegion
55534          */
55535         
55536         /**
55537          * @event beforeremove
55538          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55539          * @param {Roo.LayoutRegion} this
55540          * @param {Roo.ContentPanel} panel The panel
55541          * @param {Object} e The cancel event object
55542          */
55543         "beforeremove" : true,
55544         /**
55545          * @event invalidated
55546          * Fires when the layout for this region is changed.
55547          * @param {Roo.LayoutRegion} this
55548          */
55549         "invalidated" : true,
55550         /**
55551          * @event visibilitychange
55552          * Fires when this region is shown or hidden 
55553          * @param {Roo.LayoutRegion} this
55554          * @param {Boolean} visibility true or false
55555          */
55556         "visibilitychange" : true,
55557         /**
55558          * @event paneladded
55559          * Fires when a panel is added. 
55560          * @param {Roo.LayoutRegion} this
55561          * @param {Roo.ContentPanel} panel The panel
55562          */
55563         "paneladded" : true,
55564         /**
55565          * @event panelremoved
55566          * Fires when a panel is removed. 
55567          * @param {Roo.LayoutRegion} this
55568          * @param {Roo.ContentPanel} panel The panel
55569          */
55570         "panelremoved" : true,
55571         /**
55572          * @event beforecollapse
55573          * Fires when this region before collapse.
55574          * @param {Roo.LayoutRegion} this
55575          */
55576         "beforecollapse" : true,
55577         /**
55578          * @event collapsed
55579          * Fires when this region is collapsed.
55580          * @param {Roo.LayoutRegion} this
55581          */
55582         "collapsed" : true,
55583         /**
55584          * @event expanded
55585          * Fires when this region is expanded.
55586          * @param {Roo.LayoutRegion} this
55587          */
55588         "expanded" : true,
55589         /**
55590          * @event slideshow
55591          * Fires when this region is slid into view.
55592          * @param {Roo.LayoutRegion} this
55593          */
55594         "slideshow" : true,
55595         /**
55596          * @event slidehide
55597          * Fires when this region slides out of view. 
55598          * @param {Roo.LayoutRegion} this
55599          */
55600         "slidehide" : true,
55601         /**
55602          * @event panelactivated
55603          * Fires when a panel is activated. 
55604          * @param {Roo.LayoutRegion} this
55605          * @param {Roo.ContentPanel} panel The activated panel
55606          */
55607         "panelactivated" : true,
55608         /**
55609          * @event resized
55610          * Fires when the user resizes this region. 
55611          * @param {Roo.LayoutRegion} this
55612          * @param {Number} newSize The new size (width for east/west, height for north/south)
55613          */
55614         "resized" : true
55615     };
55616     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55617     this.panels = new Roo.util.MixedCollection();
55618     this.panels.getKey = this.getPanelId.createDelegate(this);
55619     this.box = null;
55620     this.activePanel = null;
55621     // ensure listeners are added...
55622     
55623     if (config.listeners || config.events) {
55624         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55625             listeners : config.listeners || {},
55626             events : config.events || {}
55627         });
55628     }
55629     
55630     if(skipConfig !== true){
55631         this.applyConfig(config);
55632     }
55633 };
55634
55635 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55636     getPanelId : function(p){
55637         return p.getId();
55638     },
55639     
55640     applyConfig : function(config){
55641         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55642         this.config = config;
55643         
55644     },
55645     
55646     /**
55647      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55648      * the width, for horizontal (north, south) the height.
55649      * @param {Number} newSize The new width or height
55650      */
55651     resizeTo : function(newSize){
55652         var el = this.el ? this.el :
55653                  (this.activePanel ? this.activePanel.getEl() : null);
55654         if(el){
55655             switch(this.position){
55656                 case "east":
55657                 case "west":
55658                     el.setWidth(newSize);
55659                     this.fireEvent("resized", this, newSize);
55660                 break;
55661                 case "north":
55662                 case "south":
55663                     el.setHeight(newSize);
55664                     this.fireEvent("resized", this, newSize);
55665                 break;                
55666             }
55667         }
55668     },
55669     
55670     getBox : function(){
55671         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55672     },
55673     
55674     getMargins : function(){
55675         return this.margins;
55676     },
55677     
55678     updateBox : function(box){
55679         this.box = box;
55680         var el = this.activePanel.getEl();
55681         el.dom.style.left = box.x + "px";
55682         el.dom.style.top = box.y + "px";
55683         this.activePanel.setSize(box.width, box.height);
55684     },
55685     
55686     /**
55687      * Returns the container element for this region.
55688      * @return {Roo.Element}
55689      */
55690     getEl : function(){
55691         return this.activePanel;
55692     },
55693     
55694     /**
55695      * Returns true if this region is currently visible.
55696      * @return {Boolean}
55697      */
55698     isVisible : function(){
55699         return this.activePanel ? true : false;
55700     },
55701     
55702     setActivePanel : function(panel){
55703         panel = this.getPanel(panel);
55704         if(this.activePanel && this.activePanel != panel){
55705             this.activePanel.setActiveState(false);
55706             this.activePanel.getEl().setLeftTop(-10000,-10000);
55707         }
55708         this.activePanel = panel;
55709         panel.setActiveState(true);
55710         if(this.box){
55711             panel.setSize(this.box.width, this.box.height);
55712         }
55713         this.fireEvent("panelactivated", this, panel);
55714         this.fireEvent("invalidated");
55715     },
55716     
55717     /**
55718      * Show the specified panel.
55719      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55720      * @return {Roo.ContentPanel} The shown panel or null
55721      */
55722     showPanel : function(panel){
55723         if(panel = this.getPanel(panel)){
55724             this.setActivePanel(panel);
55725         }
55726         return panel;
55727     },
55728     
55729     /**
55730      * Get the active panel for this region.
55731      * @return {Roo.ContentPanel} The active panel or null
55732      */
55733     getActivePanel : function(){
55734         return this.activePanel;
55735     },
55736     
55737     /**
55738      * Add the passed ContentPanel(s)
55739      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55740      * @return {Roo.ContentPanel} The panel added (if only one was added)
55741      */
55742     add : function(panel){
55743         if(arguments.length > 1){
55744             for(var i = 0, len = arguments.length; i < len; i++) {
55745                 this.add(arguments[i]);
55746             }
55747             return null;
55748         }
55749         if(this.hasPanel(panel)){
55750             this.showPanel(panel);
55751             return panel;
55752         }
55753         var el = panel.getEl();
55754         if(el.dom.parentNode != this.mgr.el.dom){
55755             this.mgr.el.dom.appendChild(el.dom);
55756         }
55757         if(panel.setRegion){
55758             panel.setRegion(this);
55759         }
55760         this.panels.add(panel);
55761         el.setStyle("position", "absolute");
55762         if(!panel.background){
55763             this.setActivePanel(panel);
55764             if(this.config.initialSize && this.panels.getCount()==1){
55765                 this.resizeTo(this.config.initialSize);
55766             }
55767         }
55768         this.fireEvent("paneladded", this, panel);
55769         return panel;
55770     },
55771     
55772     /**
55773      * Returns true if the panel is in this region.
55774      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55775      * @return {Boolean}
55776      */
55777     hasPanel : function(panel){
55778         if(typeof panel == "object"){ // must be panel obj
55779             panel = panel.getId();
55780         }
55781         return this.getPanel(panel) ? true : false;
55782     },
55783     
55784     /**
55785      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55786      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55787      * @param {Boolean} preservePanel Overrides the config preservePanel option
55788      * @return {Roo.ContentPanel} The panel that was removed
55789      */
55790     remove : function(panel, preservePanel){
55791         panel = this.getPanel(panel);
55792         if(!panel){
55793             return null;
55794         }
55795         var e = {};
55796         this.fireEvent("beforeremove", this, panel, e);
55797         if(e.cancel === true){
55798             return null;
55799         }
55800         var panelId = panel.getId();
55801         this.panels.removeKey(panelId);
55802         return panel;
55803     },
55804     
55805     /**
55806      * Returns the panel specified or null if it's not in this region.
55807      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55808      * @return {Roo.ContentPanel}
55809      */
55810     getPanel : function(id){
55811         if(typeof id == "object"){ // must be panel obj
55812             return id;
55813         }
55814         return this.panels.get(id);
55815     },
55816     
55817     /**
55818      * Returns this regions position (north/south/east/west/center).
55819      * @return {String} 
55820      */
55821     getPosition: function(){
55822         return this.position;    
55823     }
55824 });/*
55825  * Based on:
55826  * Ext JS Library 1.1.1
55827  * Copyright(c) 2006-2007, Ext JS, LLC.
55828  *
55829  * Originally Released Under LGPL - original licence link has changed is not relivant.
55830  *
55831  * Fork - LGPL
55832  * <script type="text/javascript">
55833  */
55834  
55835 /**
55836  * @class Roo.LayoutRegion
55837  * @extends Roo.BasicLayoutRegion
55838  * This class represents a region in a layout manager.
55839  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55840  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55841  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55842  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55843  * @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})
55844  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55845  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55846  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55847  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55848  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55849  * @cfg {String}    title           The title for the region (overrides panel titles)
55850  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55851  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55852  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55853  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55854  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55855  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55856  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55857  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55858  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55859  * @cfg {Boolean}   showPin         True to show a pin button
55860  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55861  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55862  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55863  * @cfg {Number}    width           For East/West panels
55864  * @cfg {Number}    height          For North/South panels
55865  * @cfg {Boolean}   split           To show the splitter
55866  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55867  */
55868 Roo.LayoutRegion = function(mgr, config, pos){
55869     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55870     var dh = Roo.DomHelper;
55871     /** This region's container element 
55872     * @type Roo.Element */
55873     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55874     /** This region's title element 
55875     * @type Roo.Element */
55876
55877     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55878         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55879         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55880     ]}, true);
55881     this.titleEl.enableDisplayMode();
55882     /** This region's title text element 
55883     * @type HTMLElement */
55884     this.titleTextEl = this.titleEl.dom.firstChild;
55885     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55886     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55887     this.closeBtn.enableDisplayMode();
55888     this.closeBtn.on("click", this.closeClicked, this);
55889     this.closeBtn.hide();
55890
55891     this.createBody(config);
55892     this.visible = true;
55893     this.collapsed = false;
55894
55895     if(config.hideWhenEmpty){
55896         this.hide();
55897         this.on("paneladded", this.validateVisibility, this);
55898         this.on("panelremoved", this.validateVisibility, this);
55899     }
55900     this.applyConfig(config);
55901 };
55902
55903 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55904
55905     createBody : function(){
55906         /** This region's body element 
55907         * @type Roo.Element */
55908         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55909     },
55910
55911     applyConfig : function(c){
55912         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55913             var dh = Roo.DomHelper;
55914             if(c.titlebar !== false){
55915                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55916                 this.collapseBtn.on("click", this.collapse, this);
55917                 this.collapseBtn.enableDisplayMode();
55918
55919                 if(c.showPin === true || this.showPin){
55920                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55921                     this.stickBtn.enableDisplayMode();
55922                     this.stickBtn.on("click", this.expand, this);
55923                     this.stickBtn.hide();
55924                 }
55925             }
55926             /** This region's collapsed element
55927             * @type Roo.Element */
55928             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55929                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55930             ]}, true);
55931             if(c.floatable !== false){
55932                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55933                this.collapsedEl.on("click", this.collapseClick, this);
55934             }
55935
55936             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55937                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55938                    id: "message", unselectable: "on", style:{"float":"left"}});
55939                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55940              }
55941             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55942             this.expandBtn.on("click", this.expand, this);
55943         }
55944         if(this.collapseBtn){
55945             this.collapseBtn.setVisible(c.collapsible == true);
55946         }
55947         this.cmargins = c.cmargins || this.cmargins ||
55948                          (this.position == "west" || this.position == "east" ?
55949                              {top: 0, left: 2, right:2, bottom: 0} :
55950                              {top: 2, left: 0, right:0, bottom: 2});
55951         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55952         this.bottomTabs = c.tabPosition != "top";
55953         this.autoScroll = c.autoScroll || false;
55954         if(this.autoScroll){
55955             this.bodyEl.setStyle("overflow", "auto");
55956         }else{
55957             this.bodyEl.setStyle("overflow", "hidden");
55958         }
55959         //if(c.titlebar !== false){
55960             if((!c.titlebar && !c.title) || c.titlebar === false){
55961                 this.titleEl.hide();
55962             }else{
55963                 this.titleEl.show();
55964                 if(c.title){
55965                     this.titleTextEl.innerHTML = c.title;
55966                 }
55967             }
55968         //}
55969         this.duration = c.duration || .30;
55970         this.slideDuration = c.slideDuration || .45;
55971         this.config = c;
55972         if(c.collapsed){
55973             this.collapse(true);
55974         }
55975         if(c.hidden){
55976             this.hide();
55977         }
55978     },
55979     /**
55980      * Returns true if this region is currently visible.
55981      * @return {Boolean}
55982      */
55983     isVisible : function(){
55984         return this.visible;
55985     },
55986
55987     /**
55988      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55989      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55990      */
55991     setCollapsedTitle : function(title){
55992         title = title || "&#160;";
55993         if(this.collapsedTitleTextEl){
55994             this.collapsedTitleTextEl.innerHTML = title;
55995         }
55996     },
55997
55998     getBox : function(){
55999         var b;
56000         if(!this.collapsed){
56001             b = this.el.getBox(false, true);
56002         }else{
56003             b = this.collapsedEl.getBox(false, true);
56004         }
56005         return b;
56006     },
56007
56008     getMargins : function(){
56009         return this.collapsed ? this.cmargins : this.margins;
56010     },
56011
56012     highlight : function(){
56013         this.el.addClass("x-layout-panel-dragover");
56014     },
56015
56016     unhighlight : function(){
56017         this.el.removeClass("x-layout-panel-dragover");
56018     },
56019
56020     updateBox : function(box){
56021         this.box = box;
56022         if(!this.collapsed){
56023             this.el.dom.style.left = box.x + "px";
56024             this.el.dom.style.top = box.y + "px";
56025             this.updateBody(box.width, box.height);
56026         }else{
56027             this.collapsedEl.dom.style.left = box.x + "px";
56028             this.collapsedEl.dom.style.top = box.y + "px";
56029             this.collapsedEl.setSize(box.width, box.height);
56030         }
56031         if(this.tabs){
56032             this.tabs.autoSizeTabs();
56033         }
56034     },
56035
56036     updateBody : function(w, h){
56037         if(w !== null){
56038             this.el.setWidth(w);
56039             w -= this.el.getBorderWidth("rl");
56040             if(this.config.adjustments){
56041                 w += this.config.adjustments[0];
56042             }
56043         }
56044         if(h !== null){
56045             this.el.setHeight(h);
56046             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56047             h -= this.el.getBorderWidth("tb");
56048             if(this.config.adjustments){
56049                 h += this.config.adjustments[1];
56050             }
56051             this.bodyEl.setHeight(h);
56052             if(this.tabs){
56053                 h = this.tabs.syncHeight(h);
56054             }
56055         }
56056         if(this.panelSize){
56057             w = w !== null ? w : this.panelSize.width;
56058             h = h !== null ? h : this.panelSize.height;
56059         }
56060         if(this.activePanel){
56061             var el = this.activePanel.getEl();
56062             w = w !== null ? w : el.getWidth();
56063             h = h !== null ? h : el.getHeight();
56064             this.panelSize = {width: w, height: h};
56065             this.activePanel.setSize(w, h);
56066         }
56067         if(Roo.isIE && this.tabs){
56068             this.tabs.el.repaint();
56069         }
56070     },
56071
56072     /**
56073      * Returns the container element for this region.
56074      * @return {Roo.Element}
56075      */
56076     getEl : function(){
56077         return this.el;
56078     },
56079
56080     /**
56081      * Hides this region.
56082      */
56083     hide : function(){
56084         if(!this.collapsed){
56085             this.el.dom.style.left = "-2000px";
56086             this.el.hide();
56087         }else{
56088             this.collapsedEl.dom.style.left = "-2000px";
56089             this.collapsedEl.hide();
56090         }
56091         this.visible = false;
56092         this.fireEvent("visibilitychange", this, false);
56093     },
56094
56095     /**
56096      * Shows this region if it was previously hidden.
56097      */
56098     show : function(){
56099         if(!this.collapsed){
56100             this.el.show();
56101         }else{
56102             this.collapsedEl.show();
56103         }
56104         this.visible = true;
56105         this.fireEvent("visibilitychange", this, true);
56106     },
56107
56108     closeClicked : function(){
56109         if(this.activePanel){
56110             this.remove(this.activePanel);
56111         }
56112     },
56113
56114     collapseClick : function(e){
56115         if(this.isSlid){
56116            e.stopPropagation();
56117            this.slideIn();
56118         }else{
56119            e.stopPropagation();
56120            this.slideOut();
56121         }
56122     },
56123
56124     /**
56125      * Collapses this region.
56126      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56127      */
56128     collapse : function(skipAnim, skipCheck){
56129         if(this.collapsed) {
56130             return;
56131         }
56132         
56133         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56134             
56135             this.collapsed = true;
56136             if(this.split){
56137                 this.split.el.hide();
56138             }
56139             if(this.config.animate && skipAnim !== true){
56140                 this.fireEvent("invalidated", this);
56141                 this.animateCollapse();
56142             }else{
56143                 this.el.setLocation(-20000,-20000);
56144                 this.el.hide();
56145                 this.collapsedEl.show();
56146                 this.fireEvent("collapsed", this);
56147                 this.fireEvent("invalidated", this);
56148             }
56149         }
56150         
56151     },
56152
56153     animateCollapse : function(){
56154         // overridden
56155     },
56156
56157     /**
56158      * Expands this region if it was previously collapsed.
56159      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56160      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56161      */
56162     expand : function(e, skipAnim){
56163         if(e) {
56164             e.stopPropagation();
56165         }
56166         if(!this.collapsed || this.el.hasActiveFx()) {
56167             return;
56168         }
56169         if(this.isSlid){
56170             this.afterSlideIn();
56171             skipAnim = true;
56172         }
56173         this.collapsed = false;
56174         if(this.config.animate && skipAnim !== true){
56175             this.animateExpand();
56176         }else{
56177             this.el.show();
56178             if(this.split){
56179                 this.split.el.show();
56180             }
56181             this.collapsedEl.setLocation(-2000,-2000);
56182             this.collapsedEl.hide();
56183             this.fireEvent("invalidated", this);
56184             this.fireEvent("expanded", this);
56185         }
56186     },
56187
56188     animateExpand : function(){
56189         // overridden
56190     },
56191
56192     initTabs : function()
56193     {
56194         this.bodyEl.setStyle("overflow", "hidden");
56195         var ts = new Roo.TabPanel(
56196                 this.bodyEl.dom,
56197                 {
56198                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56199                     disableTooltips: this.config.disableTabTips,
56200                     toolbar : this.config.toolbar
56201                 }
56202         );
56203         if(this.config.hideTabs){
56204             ts.stripWrap.setDisplayed(false);
56205         }
56206         this.tabs = ts;
56207         ts.resizeTabs = this.config.resizeTabs === true;
56208         ts.minTabWidth = this.config.minTabWidth || 40;
56209         ts.maxTabWidth = this.config.maxTabWidth || 250;
56210         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56211         ts.monitorResize = false;
56212         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56213         ts.bodyEl.addClass('x-layout-tabs-body');
56214         this.panels.each(this.initPanelAsTab, this);
56215     },
56216
56217     initPanelAsTab : function(panel){
56218         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56219                     this.config.closeOnTab && panel.isClosable());
56220         if(panel.tabTip !== undefined){
56221             ti.setTooltip(panel.tabTip);
56222         }
56223         ti.on("activate", function(){
56224               this.setActivePanel(panel);
56225         }, this);
56226         if(this.config.closeOnTab){
56227             ti.on("beforeclose", function(t, e){
56228                 e.cancel = true;
56229                 this.remove(panel);
56230             }, this);
56231         }
56232         return ti;
56233     },
56234
56235     updatePanelTitle : function(panel, title){
56236         if(this.activePanel == panel){
56237             this.updateTitle(title);
56238         }
56239         if(this.tabs){
56240             var ti = this.tabs.getTab(panel.getEl().id);
56241             ti.setText(title);
56242             if(panel.tabTip !== undefined){
56243                 ti.setTooltip(panel.tabTip);
56244             }
56245         }
56246     },
56247
56248     updateTitle : function(title){
56249         if(this.titleTextEl && !this.config.title){
56250             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56251         }
56252     },
56253
56254     setActivePanel : function(panel){
56255         panel = this.getPanel(panel);
56256         if(this.activePanel && this.activePanel != panel){
56257             this.activePanel.setActiveState(false);
56258         }
56259         this.activePanel = panel;
56260         panel.setActiveState(true);
56261         if(this.panelSize){
56262             panel.setSize(this.panelSize.width, this.panelSize.height);
56263         }
56264         if(this.closeBtn){
56265             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56266         }
56267         this.updateTitle(panel.getTitle());
56268         if(this.tabs){
56269             this.fireEvent("invalidated", this);
56270         }
56271         this.fireEvent("panelactivated", this, panel);
56272     },
56273
56274     /**
56275      * Shows the specified panel.
56276      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56277      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56278      */
56279     showPanel : function(panel)
56280     {
56281         panel = this.getPanel(panel);
56282         if(panel){
56283             if(this.tabs){
56284                 var tab = this.tabs.getTab(panel.getEl().id);
56285                 if(tab.isHidden()){
56286                     this.tabs.unhideTab(tab.id);
56287                 }
56288                 tab.activate();
56289             }else{
56290                 this.setActivePanel(panel);
56291             }
56292         }
56293         return panel;
56294     },
56295
56296     /**
56297      * Get the active panel for this region.
56298      * @return {Roo.ContentPanel} The active panel or null
56299      */
56300     getActivePanel : function(){
56301         return this.activePanel;
56302     },
56303
56304     validateVisibility : function(){
56305         if(this.panels.getCount() < 1){
56306             this.updateTitle("&#160;");
56307             this.closeBtn.hide();
56308             this.hide();
56309         }else{
56310             if(!this.isVisible()){
56311                 this.show();
56312             }
56313         }
56314     },
56315
56316     /**
56317      * Adds the passed ContentPanel(s) to this region.
56318      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56319      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56320      */
56321     add : function(panel){
56322         if(arguments.length > 1){
56323             for(var i = 0, len = arguments.length; i < len; i++) {
56324                 this.add(arguments[i]);
56325             }
56326             return null;
56327         }
56328         if(this.hasPanel(panel)){
56329             this.showPanel(panel);
56330             return panel;
56331         }
56332         panel.setRegion(this);
56333         this.panels.add(panel);
56334         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56335             this.bodyEl.dom.appendChild(panel.getEl().dom);
56336             if(panel.background !== true){
56337                 this.setActivePanel(panel);
56338             }
56339             this.fireEvent("paneladded", this, panel);
56340             return panel;
56341         }
56342         if(!this.tabs){
56343             this.initTabs();
56344         }else{
56345             this.initPanelAsTab(panel);
56346         }
56347         if(panel.background !== true){
56348             this.tabs.activate(panel.getEl().id);
56349         }
56350         this.fireEvent("paneladded", this, panel);
56351         return panel;
56352     },
56353
56354     /**
56355      * Hides the tab for the specified panel.
56356      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56357      */
56358     hidePanel : function(panel){
56359         if(this.tabs && (panel = this.getPanel(panel))){
56360             this.tabs.hideTab(panel.getEl().id);
56361         }
56362     },
56363
56364     /**
56365      * Unhides the tab for a previously hidden panel.
56366      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56367      */
56368     unhidePanel : function(panel){
56369         if(this.tabs && (panel = this.getPanel(panel))){
56370             this.tabs.unhideTab(panel.getEl().id);
56371         }
56372     },
56373
56374     clearPanels : function(){
56375         while(this.panels.getCount() > 0){
56376              this.remove(this.panels.first());
56377         }
56378     },
56379
56380     /**
56381      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56382      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56383      * @param {Boolean} preservePanel Overrides the config preservePanel option
56384      * @return {Roo.ContentPanel} The panel that was removed
56385      */
56386     remove : function(panel, preservePanel){
56387         panel = this.getPanel(panel);
56388         if(!panel){
56389             return null;
56390         }
56391         var e = {};
56392         this.fireEvent("beforeremove", this, panel, e);
56393         if(e.cancel === true){
56394             return null;
56395         }
56396         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56397         var panelId = panel.getId();
56398         this.panels.removeKey(panelId);
56399         if(preservePanel){
56400             document.body.appendChild(panel.getEl().dom);
56401         }
56402         if(this.tabs){
56403             this.tabs.removeTab(panel.getEl().id);
56404         }else if (!preservePanel){
56405             this.bodyEl.dom.removeChild(panel.getEl().dom);
56406         }
56407         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56408             var p = this.panels.first();
56409             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56410             tempEl.appendChild(p.getEl().dom);
56411             this.bodyEl.update("");
56412             this.bodyEl.dom.appendChild(p.getEl().dom);
56413             tempEl = null;
56414             this.updateTitle(p.getTitle());
56415             this.tabs = null;
56416             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56417             this.setActivePanel(p);
56418         }
56419         panel.setRegion(null);
56420         if(this.activePanel == panel){
56421             this.activePanel = null;
56422         }
56423         if(this.config.autoDestroy !== false && preservePanel !== true){
56424             try{panel.destroy();}catch(e){}
56425         }
56426         this.fireEvent("panelremoved", this, panel);
56427         return panel;
56428     },
56429
56430     /**
56431      * Returns the TabPanel component used by this region
56432      * @return {Roo.TabPanel}
56433      */
56434     getTabs : function(){
56435         return this.tabs;
56436     },
56437
56438     createTool : function(parentEl, className){
56439         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56440             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56441         btn.addClassOnOver("x-layout-tools-button-over");
56442         return btn;
56443     }
56444 });/*
56445  * Based on:
56446  * Ext JS Library 1.1.1
56447  * Copyright(c) 2006-2007, Ext JS, LLC.
56448  *
56449  * Originally Released Under LGPL - original licence link has changed is not relivant.
56450  *
56451  * Fork - LGPL
56452  * <script type="text/javascript">
56453  */
56454  
56455
56456
56457 /**
56458  * @class Roo.SplitLayoutRegion
56459  * @extends Roo.LayoutRegion
56460  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56461  */
56462 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56463     this.cursor = cursor;
56464     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56465 };
56466
56467 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56468     splitTip : "Drag to resize.",
56469     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56470     useSplitTips : false,
56471
56472     applyConfig : function(config){
56473         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56474         if(config.split){
56475             if(!this.split){
56476                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56477                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56478                 /** The SplitBar for this region 
56479                 * @type Roo.SplitBar */
56480                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56481                 this.split.on("moved", this.onSplitMove, this);
56482                 this.split.useShim = config.useShim === true;
56483                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56484                 if(this.useSplitTips){
56485                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56486                 }
56487                 if(config.collapsible){
56488                     this.split.el.on("dblclick", this.collapse,  this);
56489                 }
56490             }
56491             if(typeof config.minSize != "undefined"){
56492                 this.split.minSize = config.minSize;
56493             }
56494             if(typeof config.maxSize != "undefined"){
56495                 this.split.maxSize = config.maxSize;
56496             }
56497             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56498                 this.hideSplitter();
56499             }
56500         }
56501     },
56502
56503     getHMaxSize : function(){
56504          var cmax = this.config.maxSize || 10000;
56505          var center = this.mgr.getRegion("center");
56506          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56507     },
56508
56509     getVMaxSize : function(){
56510          var cmax = this.config.maxSize || 10000;
56511          var center = this.mgr.getRegion("center");
56512          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56513     },
56514
56515     onSplitMove : function(split, newSize){
56516         this.fireEvent("resized", this, newSize);
56517     },
56518     
56519     /** 
56520      * Returns the {@link Roo.SplitBar} for this region.
56521      * @return {Roo.SplitBar}
56522      */
56523     getSplitBar : function(){
56524         return this.split;
56525     },
56526     
56527     hide : function(){
56528         this.hideSplitter();
56529         Roo.SplitLayoutRegion.superclass.hide.call(this);
56530     },
56531
56532     hideSplitter : function(){
56533         if(this.split){
56534             this.split.el.setLocation(-2000,-2000);
56535             this.split.el.hide();
56536         }
56537     },
56538
56539     show : function(){
56540         if(this.split){
56541             this.split.el.show();
56542         }
56543         Roo.SplitLayoutRegion.superclass.show.call(this);
56544     },
56545     
56546     beforeSlide: function(){
56547         if(Roo.isGecko){// firefox overflow auto bug workaround
56548             this.bodyEl.clip();
56549             if(this.tabs) {
56550                 this.tabs.bodyEl.clip();
56551             }
56552             if(this.activePanel){
56553                 this.activePanel.getEl().clip();
56554                 
56555                 if(this.activePanel.beforeSlide){
56556                     this.activePanel.beforeSlide();
56557                 }
56558             }
56559         }
56560     },
56561     
56562     afterSlide : function(){
56563         if(Roo.isGecko){// firefox overflow auto bug workaround
56564             this.bodyEl.unclip();
56565             if(this.tabs) {
56566                 this.tabs.bodyEl.unclip();
56567             }
56568             if(this.activePanel){
56569                 this.activePanel.getEl().unclip();
56570                 if(this.activePanel.afterSlide){
56571                     this.activePanel.afterSlide();
56572                 }
56573             }
56574         }
56575     },
56576
56577     initAutoHide : function(){
56578         if(this.autoHide !== false){
56579             if(!this.autoHideHd){
56580                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56581                 this.autoHideHd = {
56582                     "mouseout": function(e){
56583                         if(!e.within(this.el, true)){
56584                             st.delay(500);
56585                         }
56586                     },
56587                     "mouseover" : function(e){
56588                         st.cancel();
56589                     },
56590                     scope : this
56591                 };
56592             }
56593             this.el.on(this.autoHideHd);
56594         }
56595     },
56596
56597     clearAutoHide : function(){
56598         if(this.autoHide !== false){
56599             this.el.un("mouseout", this.autoHideHd.mouseout);
56600             this.el.un("mouseover", this.autoHideHd.mouseover);
56601         }
56602     },
56603
56604     clearMonitor : function(){
56605         Roo.get(document).un("click", this.slideInIf, this);
56606     },
56607
56608     // these names are backwards but not changed for compat
56609     slideOut : function(){
56610         if(this.isSlid || this.el.hasActiveFx()){
56611             return;
56612         }
56613         this.isSlid = true;
56614         if(this.collapseBtn){
56615             this.collapseBtn.hide();
56616         }
56617         this.closeBtnState = this.closeBtn.getStyle('display');
56618         this.closeBtn.hide();
56619         if(this.stickBtn){
56620             this.stickBtn.show();
56621         }
56622         this.el.show();
56623         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56624         this.beforeSlide();
56625         this.el.setStyle("z-index", 10001);
56626         this.el.slideIn(this.getSlideAnchor(), {
56627             callback: function(){
56628                 this.afterSlide();
56629                 this.initAutoHide();
56630                 Roo.get(document).on("click", this.slideInIf, this);
56631                 this.fireEvent("slideshow", this);
56632             },
56633             scope: this,
56634             block: true
56635         });
56636     },
56637
56638     afterSlideIn : function(){
56639         this.clearAutoHide();
56640         this.isSlid = false;
56641         this.clearMonitor();
56642         this.el.setStyle("z-index", "");
56643         if(this.collapseBtn){
56644             this.collapseBtn.show();
56645         }
56646         this.closeBtn.setStyle('display', this.closeBtnState);
56647         if(this.stickBtn){
56648             this.stickBtn.hide();
56649         }
56650         this.fireEvent("slidehide", this);
56651     },
56652
56653     slideIn : function(cb){
56654         if(!this.isSlid || this.el.hasActiveFx()){
56655             Roo.callback(cb);
56656             return;
56657         }
56658         this.isSlid = false;
56659         this.beforeSlide();
56660         this.el.slideOut(this.getSlideAnchor(), {
56661             callback: function(){
56662                 this.el.setLeftTop(-10000, -10000);
56663                 this.afterSlide();
56664                 this.afterSlideIn();
56665                 Roo.callback(cb);
56666             },
56667             scope: this,
56668             block: true
56669         });
56670     },
56671     
56672     slideInIf : function(e){
56673         if(!e.within(this.el)){
56674             this.slideIn();
56675         }
56676     },
56677
56678     animateCollapse : function(){
56679         this.beforeSlide();
56680         this.el.setStyle("z-index", 20000);
56681         var anchor = this.getSlideAnchor();
56682         this.el.slideOut(anchor, {
56683             callback : function(){
56684                 this.el.setStyle("z-index", "");
56685                 this.collapsedEl.slideIn(anchor, {duration:.3});
56686                 this.afterSlide();
56687                 this.el.setLocation(-10000,-10000);
56688                 this.el.hide();
56689                 this.fireEvent("collapsed", this);
56690             },
56691             scope: this,
56692             block: true
56693         });
56694     },
56695
56696     animateExpand : function(){
56697         this.beforeSlide();
56698         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56699         this.el.setStyle("z-index", 20000);
56700         this.collapsedEl.hide({
56701             duration:.1
56702         });
56703         this.el.slideIn(this.getSlideAnchor(), {
56704             callback : function(){
56705                 this.el.setStyle("z-index", "");
56706                 this.afterSlide();
56707                 if(this.split){
56708                     this.split.el.show();
56709                 }
56710                 this.fireEvent("invalidated", this);
56711                 this.fireEvent("expanded", this);
56712             },
56713             scope: this,
56714             block: true
56715         });
56716     },
56717
56718     anchors : {
56719         "west" : "left",
56720         "east" : "right",
56721         "north" : "top",
56722         "south" : "bottom"
56723     },
56724
56725     sanchors : {
56726         "west" : "l",
56727         "east" : "r",
56728         "north" : "t",
56729         "south" : "b"
56730     },
56731
56732     canchors : {
56733         "west" : "tl-tr",
56734         "east" : "tr-tl",
56735         "north" : "tl-bl",
56736         "south" : "bl-tl"
56737     },
56738
56739     getAnchor : function(){
56740         return this.anchors[this.position];
56741     },
56742
56743     getCollapseAnchor : function(){
56744         return this.canchors[this.position];
56745     },
56746
56747     getSlideAnchor : function(){
56748         return this.sanchors[this.position];
56749     },
56750
56751     getAlignAdj : function(){
56752         var cm = this.cmargins;
56753         switch(this.position){
56754             case "west":
56755                 return [0, 0];
56756             break;
56757             case "east":
56758                 return [0, 0];
56759             break;
56760             case "north":
56761                 return [0, 0];
56762             break;
56763             case "south":
56764                 return [0, 0];
56765             break;
56766         }
56767     },
56768
56769     getExpandAdj : function(){
56770         var c = this.collapsedEl, cm = this.cmargins;
56771         switch(this.position){
56772             case "west":
56773                 return [-(cm.right+c.getWidth()+cm.left), 0];
56774             break;
56775             case "east":
56776                 return [cm.right+c.getWidth()+cm.left, 0];
56777             break;
56778             case "north":
56779                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56780             break;
56781             case "south":
56782                 return [0, cm.top+cm.bottom+c.getHeight()];
56783             break;
56784         }
56785     }
56786 });/*
56787  * Based on:
56788  * Ext JS Library 1.1.1
56789  * Copyright(c) 2006-2007, Ext JS, LLC.
56790  *
56791  * Originally Released Under LGPL - original licence link has changed is not relivant.
56792  *
56793  * Fork - LGPL
56794  * <script type="text/javascript">
56795  */
56796 /*
56797  * These classes are private internal classes
56798  */
56799 Roo.CenterLayoutRegion = function(mgr, config){
56800     Roo.LayoutRegion.call(this, mgr, config, "center");
56801     this.visible = true;
56802     this.minWidth = config.minWidth || 20;
56803     this.minHeight = config.minHeight || 20;
56804 };
56805
56806 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56807     hide : function(){
56808         // center panel can't be hidden
56809     },
56810     
56811     show : function(){
56812         // center panel can't be hidden
56813     },
56814     
56815     getMinWidth: function(){
56816         return this.minWidth;
56817     },
56818     
56819     getMinHeight: function(){
56820         return this.minHeight;
56821     }
56822 });
56823
56824
56825 Roo.NorthLayoutRegion = function(mgr, config){
56826     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56827     if(this.split){
56828         this.split.placement = Roo.SplitBar.TOP;
56829         this.split.orientation = Roo.SplitBar.VERTICAL;
56830         this.split.el.addClass("x-layout-split-v");
56831     }
56832     var size = config.initialSize || config.height;
56833     if(typeof size != "undefined"){
56834         this.el.setHeight(size);
56835     }
56836 };
56837 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56838     orientation: Roo.SplitBar.VERTICAL,
56839     getBox : function(){
56840         if(this.collapsed){
56841             return this.collapsedEl.getBox();
56842         }
56843         var box = this.el.getBox();
56844         if(this.split){
56845             box.height += this.split.el.getHeight();
56846         }
56847         return box;
56848     },
56849     
56850     updateBox : function(box){
56851         if(this.split && !this.collapsed){
56852             box.height -= this.split.el.getHeight();
56853             this.split.el.setLeft(box.x);
56854             this.split.el.setTop(box.y+box.height);
56855             this.split.el.setWidth(box.width);
56856         }
56857         if(this.collapsed){
56858             this.updateBody(box.width, null);
56859         }
56860         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56861     }
56862 });
56863
56864 Roo.SouthLayoutRegion = function(mgr, config){
56865     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56866     if(this.split){
56867         this.split.placement = Roo.SplitBar.BOTTOM;
56868         this.split.orientation = Roo.SplitBar.VERTICAL;
56869         this.split.el.addClass("x-layout-split-v");
56870     }
56871     var size = config.initialSize || config.height;
56872     if(typeof size != "undefined"){
56873         this.el.setHeight(size);
56874     }
56875 };
56876 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56877     orientation: Roo.SplitBar.VERTICAL,
56878     getBox : function(){
56879         if(this.collapsed){
56880             return this.collapsedEl.getBox();
56881         }
56882         var box = this.el.getBox();
56883         if(this.split){
56884             var sh = this.split.el.getHeight();
56885             box.height += sh;
56886             box.y -= sh;
56887         }
56888         return box;
56889     },
56890     
56891     updateBox : function(box){
56892         if(this.split && !this.collapsed){
56893             var sh = this.split.el.getHeight();
56894             box.height -= sh;
56895             box.y += sh;
56896             this.split.el.setLeft(box.x);
56897             this.split.el.setTop(box.y-sh);
56898             this.split.el.setWidth(box.width);
56899         }
56900         if(this.collapsed){
56901             this.updateBody(box.width, null);
56902         }
56903         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56904     }
56905 });
56906
56907 Roo.EastLayoutRegion = function(mgr, config){
56908     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56909     if(this.split){
56910         this.split.placement = Roo.SplitBar.RIGHT;
56911         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56912         this.split.el.addClass("x-layout-split-h");
56913     }
56914     var size = config.initialSize || config.width;
56915     if(typeof size != "undefined"){
56916         this.el.setWidth(size);
56917     }
56918 };
56919 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56920     orientation: Roo.SplitBar.HORIZONTAL,
56921     getBox : function(){
56922         if(this.collapsed){
56923             return this.collapsedEl.getBox();
56924         }
56925         var box = this.el.getBox();
56926         if(this.split){
56927             var sw = this.split.el.getWidth();
56928             box.width += sw;
56929             box.x -= sw;
56930         }
56931         return box;
56932     },
56933
56934     updateBox : function(box){
56935         if(this.split && !this.collapsed){
56936             var sw = this.split.el.getWidth();
56937             box.width -= sw;
56938             this.split.el.setLeft(box.x);
56939             this.split.el.setTop(box.y);
56940             this.split.el.setHeight(box.height);
56941             box.x += sw;
56942         }
56943         if(this.collapsed){
56944             this.updateBody(null, box.height);
56945         }
56946         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56947     }
56948 });
56949
56950 Roo.WestLayoutRegion = function(mgr, config){
56951     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56952     if(this.split){
56953         this.split.placement = Roo.SplitBar.LEFT;
56954         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56955         this.split.el.addClass("x-layout-split-h");
56956     }
56957     var size = config.initialSize || config.width;
56958     if(typeof size != "undefined"){
56959         this.el.setWidth(size);
56960     }
56961 };
56962 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56963     orientation: Roo.SplitBar.HORIZONTAL,
56964     getBox : function(){
56965         if(this.collapsed){
56966             return this.collapsedEl.getBox();
56967         }
56968         var box = this.el.getBox();
56969         if(this.split){
56970             box.width += this.split.el.getWidth();
56971         }
56972         return box;
56973     },
56974     
56975     updateBox : function(box){
56976         if(this.split && !this.collapsed){
56977             var sw = this.split.el.getWidth();
56978             box.width -= sw;
56979             this.split.el.setLeft(box.x+box.width);
56980             this.split.el.setTop(box.y);
56981             this.split.el.setHeight(box.height);
56982         }
56983         if(this.collapsed){
56984             this.updateBody(null, box.height);
56985         }
56986         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56987     }
56988 });
56989 /*
56990  * Based on:
56991  * Ext JS Library 1.1.1
56992  * Copyright(c) 2006-2007, Ext JS, LLC.
56993  *
56994  * Originally Released Under LGPL - original licence link has changed is not relivant.
56995  *
56996  * Fork - LGPL
56997  * <script type="text/javascript">
56998  */
56999  
57000  
57001 /*
57002  * Private internal class for reading and applying state
57003  */
57004 Roo.LayoutStateManager = function(layout){
57005      // default empty state
57006      this.state = {
57007         north: {},
57008         south: {},
57009         east: {},
57010         west: {}       
57011     };
57012 };
57013
57014 Roo.LayoutStateManager.prototype = {
57015     init : function(layout, provider){
57016         this.provider = provider;
57017         var state = provider.get(layout.id+"-layout-state");
57018         if(state){
57019             var wasUpdating = layout.isUpdating();
57020             if(!wasUpdating){
57021                 layout.beginUpdate();
57022             }
57023             for(var key in state){
57024                 if(typeof state[key] != "function"){
57025                     var rstate = state[key];
57026                     var r = layout.getRegion(key);
57027                     if(r && rstate){
57028                         if(rstate.size){
57029                             r.resizeTo(rstate.size);
57030                         }
57031                         if(rstate.collapsed == true){
57032                             r.collapse(true);
57033                         }else{
57034                             r.expand(null, true);
57035                         }
57036                     }
57037                 }
57038             }
57039             if(!wasUpdating){
57040                 layout.endUpdate();
57041             }
57042             this.state = state; 
57043         }
57044         this.layout = layout;
57045         layout.on("regionresized", this.onRegionResized, this);
57046         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57047         layout.on("regionexpanded", this.onRegionExpanded, this);
57048     },
57049     
57050     storeState : function(){
57051         this.provider.set(this.layout.id+"-layout-state", this.state);
57052     },
57053     
57054     onRegionResized : function(region, newSize){
57055         this.state[region.getPosition()].size = newSize;
57056         this.storeState();
57057     },
57058     
57059     onRegionCollapsed : function(region){
57060         this.state[region.getPosition()].collapsed = true;
57061         this.storeState();
57062     },
57063     
57064     onRegionExpanded : function(region){
57065         this.state[region.getPosition()].collapsed = false;
57066         this.storeState();
57067     }
57068 };/*
57069  * Based on:
57070  * Ext JS Library 1.1.1
57071  * Copyright(c) 2006-2007, Ext JS, LLC.
57072  *
57073  * Originally Released Under LGPL - original licence link has changed is not relivant.
57074  *
57075  * Fork - LGPL
57076  * <script type="text/javascript">
57077  */
57078 /**
57079  * @class Roo.ContentPanel
57080  * @extends Roo.util.Observable
57081  * @children Roo.form.Form Roo.JsonView Roo.View
57082  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57083  * A basic ContentPanel element.
57084  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57085  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57086  * @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
57087  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57088  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57089  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57090  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57091  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57092  * @cfg {String} title          The title for this panel
57093  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57094  * @cfg {String} url            Calls {@link #setUrl} with this value
57095  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57096  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57097  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57098  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57099  * @cfg {String}    style  Extra style to add to the content panel
57100  * @cfg {Roo.menu.Menu} menu  popup menu
57101
57102  * @constructor
57103  * Create a new ContentPanel.
57104  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57105  * @param {String/Object} config A string to set only the title or a config object
57106  * @param {String} content (optional) Set the HTML content for this panel
57107  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57108  */
57109 Roo.ContentPanel = function(el, config, content){
57110     
57111      
57112     /*
57113     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57114         config = el;
57115         el = Roo.id();
57116     }
57117     if (config && config.parentLayout) { 
57118         el = config.parentLayout.el.createChild(); 
57119     }
57120     */
57121     if(el.autoCreate){ // xtype is available if this is called from factory
57122         config = el;
57123         el = Roo.id();
57124     }
57125     this.el = Roo.get(el);
57126     if(!this.el && config && config.autoCreate){
57127         if(typeof config.autoCreate == "object"){
57128             if(!config.autoCreate.id){
57129                 config.autoCreate.id = config.id||el;
57130             }
57131             this.el = Roo.DomHelper.append(document.body,
57132                         config.autoCreate, true);
57133         }else{
57134             this.el = Roo.DomHelper.append(document.body,
57135                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57136         }
57137     }
57138     
57139     
57140     this.closable = false;
57141     this.loaded = false;
57142     this.active = false;
57143     if(typeof config == "string"){
57144         this.title = config;
57145     }else{
57146         Roo.apply(this, config);
57147     }
57148     
57149     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57150         this.wrapEl = this.el.wrap();
57151         this.toolbar.container = this.el.insertSibling(false, 'before');
57152         this.toolbar = new Roo.Toolbar(this.toolbar);
57153     }
57154     
57155     // xtype created footer. - not sure if will work as we normally have to render first..
57156     if (this.footer && !this.footer.el && this.footer.xtype) {
57157         if (!this.wrapEl) {
57158             this.wrapEl = this.el.wrap();
57159         }
57160     
57161         this.footer.container = this.wrapEl.createChild();
57162          
57163         this.footer = Roo.factory(this.footer, Roo);
57164         
57165     }
57166     
57167     if(this.resizeEl){
57168         this.resizeEl = Roo.get(this.resizeEl, true);
57169     }else{
57170         this.resizeEl = this.el;
57171     }
57172     // handle view.xtype
57173     
57174  
57175     
57176     
57177     this.addEvents({
57178         /**
57179          * @event activate
57180          * Fires when this panel is activated. 
57181          * @param {Roo.ContentPanel} this
57182          */
57183         "activate" : true,
57184         /**
57185          * @event deactivate
57186          * Fires when this panel is activated. 
57187          * @param {Roo.ContentPanel} this
57188          */
57189         "deactivate" : true,
57190
57191         /**
57192          * @event resize
57193          * Fires when this panel is resized if fitToFrame is true.
57194          * @param {Roo.ContentPanel} this
57195          * @param {Number} width The width after any component adjustments
57196          * @param {Number} height The height after any component adjustments
57197          */
57198         "resize" : true,
57199         
57200          /**
57201          * @event render
57202          * Fires when this tab is created
57203          * @param {Roo.ContentPanel} this
57204          */
57205         "render" : true
57206          
57207         
57208     });
57209     
57210
57211     
57212     
57213     if(this.autoScroll){
57214         this.resizeEl.setStyle("overflow", "auto");
57215     } else {
57216         // fix randome scrolling
57217         this.el.on('scroll', function() {
57218             Roo.log('fix random scolling');
57219             this.scrollTo('top',0); 
57220         });
57221     }
57222     content = content || this.content;
57223     if(content){
57224         this.setContent(content);
57225     }
57226     if(config && config.url){
57227         this.setUrl(this.url, this.params, this.loadOnce);
57228     }
57229     
57230     
57231     
57232     Roo.ContentPanel.superclass.constructor.call(this);
57233     
57234     if (this.view && typeof(this.view.xtype) != 'undefined') {
57235         this.view.el = this.el.appendChild(document.createElement("div"));
57236         this.view = Roo.factory(this.view); 
57237         this.view.render  &&  this.view.render(false, '');  
57238     }
57239     
57240     
57241     this.fireEvent('render', this);
57242 };
57243
57244 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57245     tabTip:'',
57246     setRegion : function(region){
57247         this.region = region;
57248         if(region){
57249            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57250         }else{
57251            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57252         } 
57253     },
57254     
57255     /**
57256      * Returns the toolbar for this Panel if one was configured. 
57257      * @return {Roo.Toolbar} 
57258      */
57259     getToolbar : function(){
57260         return this.toolbar;
57261     },
57262     
57263     setActiveState : function(active){
57264         this.active = active;
57265         if(!active){
57266             this.fireEvent("deactivate", this);
57267         }else{
57268             this.fireEvent("activate", this);
57269         }
57270     },
57271     /**
57272      * Updates this panel's element
57273      * @param {String} content The new content
57274      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57275     */
57276     setContent : function(content, loadScripts){
57277         this.el.update(content, loadScripts);
57278     },
57279
57280     ignoreResize : function(w, h){
57281         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57282             return true;
57283         }else{
57284             this.lastSize = {width: w, height: h};
57285             return false;
57286         }
57287     },
57288     /**
57289      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57290      * @return {Roo.UpdateManager} The UpdateManager
57291      */
57292     getUpdateManager : function(){
57293         return this.el.getUpdateManager();
57294     },
57295      /**
57296      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57297      * @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:
57298 <pre><code>
57299 panel.load({
57300     url: "your-url.php",
57301     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57302     callback: yourFunction,
57303     scope: yourObject, //(optional scope)
57304     discardUrl: false,
57305     nocache: false,
57306     text: "Loading...",
57307     timeout: 30,
57308     scripts: false
57309 });
57310 </code></pre>
57311      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57312      * 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.
57313      * @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}
57314      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57315      * @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.
57316      * @return {Roo.ContentPanel} this
57317      */
57318     load : function(){
57319         var um = this.el.getUpdateManager();
57320         um.update.apply(um, arguments);
57321         return this;
57322     },
57323
57324
57325     /**
57326      * 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.
57327      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57328      * @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)
57329      * @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)
57330      * @return {Roo.UpdateManager} The UpdateManager
57331      */
57332     setUrl : function(url, params, loadOnce){
57333         if(this.refreshDelegate){
57334             this.removeListener("activate", this.refreshDelegate);
57335         }
57336         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57337         this.on("activate", this.refreshDelegate);
57338         return this.el.getUpdateManager();
57339     },
57340     
57341     _handleRefresh : function(url, params, loadOnce){
57342         if(!loadOnce || !this.loaded){
57343             var updater = this.el.getUpdateManager();
57344             updater.update(url, params, this._setLoaded.createDelegate(this));
57345         }
57346     },
57347     
57348     _setLoaded : function(){
57349         this.loaded = true;
57350     }, 
57351     
57352     /**
57353      * Returns this panel's id
57354      * @return {String} 
57355      */
57356     getId : function(){
57357         return this.el.id;
57358     },
57359     
57360     /** 
57361      * Returns this panel's element - used by regiosn to add.
57362      * @return {Roo.Element} 
57363      */
57364     getEl : function(){
57365         return this.wrapEl || this.el;
57366     },
57367     
57368     adjustForComponents : function(width, height)
57369     {
57370         //Roo.log('adjustForComponents ');
57371         if(this.resizeEl != this.el){
57372             width -= this.el.getFrameWidth('lr');
57373             height -= this.el.getFrameWidth('tb');
57374         }
57375         if(this.toolbar){
57376             var te = this.toolbar.getEl();
57377             height -= te.getHeight();
57378             te.setWidth(width);
57379         }
57380         if(this.footer){
57381             var te = this.footer.getEl();
57382             //Roo.log("footer:" + te.getHeight());
57383             
57384             height -= te.getHeight();
57385             te.setWidth(width);
57386         }
57387         
57388         
57389         if(this.adjustments){
57390             width += this.adjustments[0];
57391             height += this.adjustments[1];
57392         }
57393         return {"width": width, "height": height};
57394     },
57395     
57396     setSize : function(width, height){
57397         if(this.fitToFrame && !this.ignoreResize(width, height)){
57398             if(this.fitContainer && this.resizeEl != this.el){
57399                 this.el.setSize(width, height);
57400             }
57401             var size = this.adjustForComponents(width, height);
57402             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57403             this.fireEvent('resize', this, size.width, size.height);
57404         }
57405     },
57406     
57407     /**
57408      * Returns this panel's title
57409      * @return {String} 
57410      */
57411     getTitle : function(){
57412         return this.title;
57413     },
57414     
57415     /**
57416      * Set this panel's title
57417      * @param {String} title
57418      */
57419     setTitle : function(title){
57420         this.title = title;
57421         if(this.region){
57422             this.region.updatePanelTitle(this, title);
57423         }
57424     },
57425     
57426     /**
57427      * Returns true is this panel was configured to be closable
57428      * @return {Boolean} 
57429      */
57430     isClosable : function(){
57431         return this.closable;
57432     },
57433     
57434     beforeSlide : function(){
57435         this.el.clip();
57436         this.resizeEl.clip();
57437     },
57438     
57439     afterSlide : function(){
57440         this.el.unclip();
57441         this.resizeEl.unclip();
57442     },
57443     
57444     /**
57445      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57446      *   Will fail silently if the {@link #setUrl} method has not been called.
57447      *   This does not activate the panel, just updates its content.
57448      */
57449     refresh : function(){
57450         if(this.refreshDelegate){
57451            this.loaded = false;
57452            this.refreshDelegate();
57453         }
57454     },
57455     
57456     /**
57457      * Destroys this panel
57458      */
57459     destroy : function(){
57460         this.el.removeAllListeners();
57461         var tempEl = document.createElement("span");
57462         tempEl.appendChild(this.el.dom);
57463         tempEl.innerHTML = "";
57464         this.el.remove();
57465         this.el = null;
57466     },
57467     
57468     /**
57469      * form - if the content panel contains a form - this is a reference to it.
57470      * @type {Roo.form.Form}
57471      */
57472     form : false,
57473     /**
57474      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57475      *    This contains a reference to it.
57476      * @type {Roo.View}
57477      */
57478     view : false,
57479     
57480       /**
57481      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57482      * <pre><code>
57483
57484 layout.addxtype({
57485        xtype : 'Form',
57486        items: [ .... ]
57487    }
57488 );
57489
57490 </code></pre>
57491      * @param {Object} cfg Xtype definition of item to add.
57492      */
57493     
57494     addxtype : function(cfg) {
57495         // add form..
57496         if (cfg.xtype.match(/^Form$/)) {
57497             
57498             var el;
57499             //if (this.footer) {
57500             //    el = this.footer.container.insertSibling(false, 'before');
57501             //} else {
57502                 el = this.el.createChild();
57503             //}
57504
57505             this.form = new  Roo.form.Form(cfg);
57506             
57507             
57508             if ( this.form.allItems.length) {
57509                 this.form.render(el.dom);
57510             }
57511             return this.form;
57512         }
57513         // should only have one of theses..
57514         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57515             // views.. should not be just added - used named prop 'view''
57516             
57517             cfg.el = this.el.appendChild(document.createElement("div"));
57518             // factory?
57519             
57520             var ret = new Roo.factory(cfg);
57521              
57522              ret.render && ret.render(false, ''); // render blank..
57523             this.view = ret;
57524             return ret;
57525         }
57526         return false;
57527     }
57528 });
57529
57530 /**
57531  * @class Roo.GridPanel
57532  * @extends Roo.ContentPanel
57533  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57534  * @constructor
57535  * Create a new GridPanel.
57536  * @cfg {Roo.grid.Grid} grid The grid for this panel
57537  */
57538 Roo.GridPanel = function(grid, config){
57539     
57540     // universal ctor...
57541     if (typeof(grid.grid) != 'undefined') {
57542         config = grid;
57543         grid = config.grid;
57544     }
57545     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57546         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57547         
57548     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57549     
57550     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57551     
57552     if(this.toolbar){
57553         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57554     }
57555     // xtype created footer. - not sure if will work as we normally have to render first..
57556     if (this.footer && !this.footer.el && this.footer.xtype) {
57557         
57558         this.footer.container = this.grid.getView().getFooterPanel(true);
57559         this.footer.dataSource = this.grid.dataSource;
57560         this.footer = Roo.factory(this.footer, Roo);
57561         
57562     }
57563     
57564     grid.monitorWindowResize = false; // turn off autosizing
57565     grid.autoHeight = false;
57566     grid.autoWidth = false;
57567     this.grid = grid;
57568     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57569 };
57570
57571 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57572     getId : function(){
57573         return this.grid.id;
57574     },
57575     
57576     /**
57577      * Returns the grid for this panel
57578      * @return {Roo.grid.Grid} 
57579      */
57580     getGrid : function(){
57581         return this.grid;    
57582     },
57583     
57584     setSize : function(width, height){
57585         if(!this.ignoreResize(width, height)){
57586             var grid = this.grid;
57587             var size = this.adjustForComponents(width, height);
57588             grid.getGridEl().setSize(size.width, size.height);
57589             grid.autoSize();
57590         }
57591     },
57592     
57593     beforeSlide : function(){
57594         this.grid.getView().scroller.clip();
57595     },
57596     
57597     afterSlide : function(){
57598         this.grid.getView().scroller.unclip();
57599     },
57600     
57601     destroy : function(){
57602         this.grid.destroy();
57603         delete this.grid;
57604         Roo.GridPanel.superclass.destroy.call(this); 
57605     }
57606 });
57607
57608
57609 /**
57610  * @class Roo.NestedLayoutPanel
57611  * @extends Roo.ContentPanel
57612  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57613  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57614  *
57615  * 
57616  * @constructor
57617  * Create a new NestedLayoutPanel.
57618  * 
57619  * 
57620  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57621  * @param {String/Object} config A string to set only the title or a config object
57622  */
57623 Roo.NestedLayoutPanel = function(layout, config)
57624 {
57625     // construct with only one argument..
57626     /* FIXME - implement nicer consturctors
57627     if (layout.layout) {
57628         config = layout;
57629         layout = config.layout;
57630         delete config.layout;
57631     }
57632     if (layout.xtype && !layout.getEl) {
57633         // then layout needs constructing..
57634         layout = Roo.factory(layout, Roo);
57635     }
57636     */
57637     
57638     
57639     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57640     
57641     layout.monitorWindowResize = false; // turn off autosizing
57642     this.layout = layout;
57643     this.layout.getEl().addClass("x-layout-nested-layout");
57644     
57645     
57646     
57647     
57648 };
57649
57650 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57651
57652     setSize : function(width, height){
57653         if(!this.ignoreResize(width, height)){
57654             var size = this.adjustForComponents(width, height);
57655             var el = this.layout.getEl();
57656             el.setSize(size.width, size.height);
57657             var touch = el.dom.offsetWidth;
57658             this.layout.layout();
57659             // ie requires a double layout on the first pass
57660             if(Roo.isIE && !this.initialized){
57661                 this.initialized = true;
57662                 this.layout.layout();
57663             }
57664         }
57665     },
57666     
57667     // activate all subpanels if not currently active..
57668     
57669     setActiveState : function(active){
57670         this.active = active;
57671         if(!active){
57672             this.fireEvent("deactivate", this);
57673             return;
57674         }
57675         
57676         this.fireEvent("activate", this);
57677         // not sure if this should happen before or after..
57678         if (!this.layout) {
57679             return; // should not happen..
57680         }
57681         var reg = false;
57682         for (var r in this.layout.regions) {
57683             reg = this.layout.getRegion(r);
57684             if (reg.getActivePanel()) {
57685                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57686                 reg.setActivePanel(reg.getActivePanel());
57687                 continue;
57688             }
57689             if (!reg.panels.length) {
57690                 continue;
57691             }
57692             reg.showPanel(reg.getPanel(0));
57693         }
57694         
57695         
57696         
57697         
57698     },
57699     
57700     /**
57701      * Returns the nested BorderLayout for this panel
57702      * @return {Roo.BorderLayout} 
57703      */
57704     getLayout : function(){
57705         return this.layout;
57706     },
57707     
57708      /**
57709      * Adds a xtype elements to the layout of the nested panel
57710      * <pre><code>
57711
57712 panel.addxtype({
57713        xtype : 'ContentPanel',
57714        region: 'west',
57715        items: [ .... ]
57716    }
57717 );
57718
57719 panel.addxtype({
57720         xtype : 'NestedLayoutPanel',
57721         region: 'west',
57722         layout: {
57723            center: { },
57724            west: { }   
57725         },
57726         items : [ ... list of content panels or nested layout panels.. ]
57727    }
57728 );
57729 </code></pre>
57730      * @param {Object} cfg Xtype definition of item to add.
57731      */
57732     addxtype : function(cfg) {
57733         return this.layout.addxtype(cfg);
57734     
57735     }
57736 });
57737
57738 Roo.ScrollPanel = function(el, config, content){
57739     config = config || {};
57740     config.fitToFrame = true;
57741     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57742     
57743     this.el.dom.style.overflow = "hidden";
57744     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57745     this.el.removeClass("x-layout-inactive-content");
57746     this.el.on("mousewheel", this.onWheel, this);
57747
57748     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57749     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57750     up.unselectable(); down.unselectable();
57751     up.on("click", this.scrollUp, this);
57752     down.on("click", this.scrollDown, this);
57753     up.addClassOnOver("x-scroller-btn-over");
57754     down.addClassOnOver("x-scroller-btn-over");
57755     up.addClassOnClick("x-scroller-btn-click");
57756     down.addClassOnClick("x-scroller-btn-click");
57757     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57758
57759     this.resizeEl = this.el;
57760     this.el = wrap; this.up = up; this.down = down;
57761 };
57762
57763 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57764     increment : 100,
57765     wheelIncrement : 5,
57766     scrollUp : function(){
57767         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57768     },
57769
57770     scrollDown : function(){
57771         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57772     },
57773
57774     afterScroll : function(){
57775         var el = this.resizeEl;
57776         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57777         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57778         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57779     },
57780
57781     setSize : function(){
57782         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57783         this.afterScroll();
57784     },
57785
57786     onWheel : function(e){
57787         var d = e.getWheelDelta();
57788         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57789         this.afterScroll();
57790         e.stopEvent();
57791     },
57792
57793     setContent : function(content, loadScripts){
57794         this.resizeEl.update(content, loadScripts);
57795     }
57796
57797 });
57798
57799
57800
57801 /**
57802  * @class Roo.TreePanel
57803  * @extends Roo.ContentPanel
57804  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57805  * Treepanel component
57806  * 
57807  * @constructor
57808  * Create a new TreePanel. - defaults to fit/scoll contents.
57809  * @param {String/Object} config A string to set only the panel's title, or a config object
57810  */
57811 Roo.TreePanel = function(config){
57812     var el = config.el;
57813     var tree = config.tree;
57814     delete config.tree; 
57815     delete config.el; // hopefull!
57816     
57817     // wrapper for IE7 strict & safari scroll issue
57818     
57819     var treeEl = el.createChild();
57820     config.resizeEl = treeEl;
57821     
57822     
57823     
57824     Roo.TreePanel.superclass.constructor.call(this, el, config);
57825  
57826  
57827     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57828     //console.log(tree);
57829     this.on('activate', function()
57830     {
57831         if (this.tree.rendered) {
57832             return;
57833         }
57834         //console.log('render tree');
57835         this.tree.render();
57836     });
57837     // this should not be needed.. - it's actually the 'el' that resizes?
57838     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57839     
57840     //this.on('resize',  function (cp, w, h) {
57841     //        this.tree.innerCt.setWidth(w);
57842     //        this.tree.innerCt.setHeight(h);
57843     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57844     //});
57845
57846         
57847     
57848 };
57849
57850 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57851     fitToFrame : true,
57852     autoScroll : true,
57853     /*
57854      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57855      */
57856     tree : false
57857
57858 });
57859
57860
57861
57862
57863
57864
57865
57866
57867
57868
57869
57870 /*
57871  * Based on:
57872  * Ext JS Library 1.1.1
57873  * Copyright(c) 2006-2007, Ext JS, LLC.
57874  *
57875  * Originally Released Under LGPL - original licence link has changed is not relivant.
57876  *
57877  * Fork - LGPL
57878  * <script type="text/javascript">
57879  */
57880  
57881
57882 /**
57883  * @class Roo.ReaderLayout
57884  * @extends Roo.BorderLayout
57885  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57886  * center region containing two nested regions (a top one for a list view and one for item preview below),
57887  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57888  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57889  * expedites the setup of the overall layout and regions for this common application style.
57890  * Example:
57891  <pre><code>
57892 var reader = new Roo.ReaderLayout();
57893 var CP = Roo.ContentPanel;  // shortcut for adding
57894
57895 reader.beginUpdate();
57896 reader.add("north", new CP("north", "North"));
57897 reader.add("west", new CP("west", {title: "West"}));
57898 reader.add("east", new CP("east", {title: "East"}));
57899
57900 reader.regions.listView.add(new CP("listView", "List"));
57901 reader.regions.preview.add(new CP("preview", "Preview"));
57902 reader.endUpdate();
57903 </code></pre>
57904 * @constructor
57905 * Create a new ReaderLayout
57906 * @param {Object} config Configuration options
57907 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57908 * document.body if omitted)
57909 */
57910 Roo.ReaderLayout = function(config, renderTo){
57911     var c = config || {size:{}};
57912     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57913         north: c.north !== false ? Roo.apply({
57914             split:false,
57915             initialSize: 32,
57916             titlebar: false
57917         }, c.north) : false,
57918         west: c.west !== false ? Roo.apply({
57919             split:true,
57920             initialSize: 200,
57921             minSize: 175,
57922             maxSize: 400,
57923             titlebar: true,
57924             collapsible: true,
57925             animate: true,
57926             margins:{left:5,right:0,bottom:5,top:5},
57927             cmargins:{left:5,right:5,bottom:5,top:5}
57928         }, c.west) : false,
57929         east: c.east !== false ? Roo.apply({
57930             split:true,
57931             initialSize: 200,
57932             minSize: 175,
57933             maxSize: 400,
57934             titlebar: true,
57935             collapsible: true,
57936             animate: true,
57937             margins:{left:0,right:5,bottom:5,top:5},
57938             cmargins:{left:5,right:5,bottom:5,top:5}
57939         }, c.east) : false,
57940         center: Roo.apply({
57941             tabPosition: 'top',
57942             autoScroll:false,
57943             closeOnTab: true,
57944             titlebar:false,
57945             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57946         }, c.center)
57947     });
57948
57949     this.el.addClass('x-reader');
57950
57951     this.beginUpdate();
57952
57953     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57954         south: c.preview !== false ? Roo.apply({
57955             split:true,
57956             initialSize: 200,
57957             minSize: 100,
57958             autoScroll:true,
57959             collapsible:true,
57960             titlebar: true,
57961             cmargins:{top:5,left:0, right:0, bottom:0}
57962         }, c.preview) : false,
57963         center: Roo.apply({
57964             autoScroll:false,
57965             titlebar:false,
57966             minHeight:200
57967         }, c.listView)
57968     });
57969     this.add('center', new Roo.NestedLayoutPanel(inner,
57970             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57971
57972     this.endUpdate();
57973
57974     this.regions.preview = inner.getRegion('south');
57975     this.regions.listView = inner.getRegion('center');
57976 };
57977
57978 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57979  * Based on:
57980  * Ext JS Library 1.1.1
57981  * Copyright(c) 2006-2007, Ext JS, LLC.
57982  *
57983  * Originally Released Under LGPL - original licence link has changed is not relivant.
57984  *
57985  * Fork - LGPL
57986  * <script type="text/javascript">
57987  */
57988  
57989 /**
57990  * @class Roo.grid.Grid
57991  * @extends Roo.util.Observable
57992  * This class represents the primary interface of a component based grid control.
57993  * <br><br>Usage:<pre><code>
57994  var grid = new Roo.grid.Grid("my-container-id", {
57995      ds: myDataStore,
57996      cm: myColModel,
57997      selModel: mySelectionModel,
57998      autoSizeColumns: true,
57999      monitorWindowResize: false,
58000      trackMouseOver: true
58001  });
58002  // set any options
58003  grid.render();
58004  * </code></pre>
58005  * <b>Common Problems:</b><br/>
58006  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
58007  * element will correct this<br/>
58008  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
58009  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
58010  * are unpredictable.<br/>
58011  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
58012  * grid to calculate dimensions/offsets.<br/>
58013   * @constructor
58014  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58015  * The container MUST have some type of size defined for the grid to fill. The container will be
58016  * automatically set to position relative if it isn't already.
58017  * @param {Object} config A config object that sets properties on this grid.
58018  */
58019 Roo.grid.Grid = function(container, config){
58020         // initialize the container
58021         this.container = Roo.get(container);
58022         this.container.update("");
58023         this.container.setStyle("overflow", "hidden");
58024     this.container.addClass('x-grid-container');
58025
58026     this.id = this.container.id;
58027
58028     Roo.apply(this, config);
58029     // check and correct shorthanded configs
58030     if(this.ds){
58031         this.dataSource = this.ds;
58032         delete this.ds;
58033     }
58034     if(this.cm){
58035         this.colModel = this.cm;
58036         delete this.cm;
58037     }
58038     if(this.sm){
58039         this.selModel = this.sm;
58040         delete this.sm;
58041     }
58042
58043     if (this.selModel) {
58044         this.selModel = Roo.factory(this.selModel, Roo.grid);
58045         this.sm = this.selModel;
58046         this.sm.xmodule = this.xmodule || false;
58047     }
58048     if (typeof(this.colModel.config) == 'undefined') {
58049         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58050         this.cm = this.colModel;
58051         this.cm.xmodule = this.xmodule || false;
58052     }
58053     if (this.dataSource) {
58054         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58055         this.ds = this.dataSource;
58056         this.ds.xmodule = this.xmodule || false;
58057          
58058     }
58059     
58060     
58061     
58062     if(this.width){
58063         this.container.setWidth(this.width);
58064     }
58065
58066     if(this.height){
58067         this.container.setHeight(this.height);
58068     }
58069     /** @private */
58070         this.addEvents({
58071         // raw events
58072         /**
58073          * @event click
58074          * The raw click event for the entire grid.
58075          * @param {Roo.EventObject} e
58076          */
58077         "click" : true,
58078         /**
58079          * @event dblclick
58080          * The raw dblclick event for the entire grid.
58081          * @param {Roo.EventObject} e
58082          */
58083         "dblclick" : true,
58084         /**
58085          * @event contextmenu
58086          * The raw contextmenu event for the entire grid.
58087          * @param {Roo.EventObject} e
58088          */
58089         "contextmenu" : true,
58090         /**
58091          * @event mousedown
58092          * The raw mousedown event for the entire grid.
58093          * @param {Roo.EventObject} e
58094          */
58095         "mousedown" : true,
58096         /**
58097          * @event mouseup
58098          * The raw mouseup event for the entire grid.
58099          * @param {Roo.EventObject} e
58100          */
58101         "mouseup" : true,
58102         /**
58103          * @event mouseover
58104          * The raw mouseover event for the entire grid.
58105          * @param {Roo.EventObject} e
58106          */
58107         "mouseover" : true,
58108         /**
58109          * @event mouseout
58110          * The raw mouseout event for the entire grid.
58111          * @param {Roo.EventObject} e
58112          */
58113         "mouseout" : true,
58114         /**
58115          * @event keypress
58116          * The raw keypress event for the entire grid.
58117          * @param {Roo.EventObject} e
58118          */
58119         "keypress" : true,
58120         /**
58121          * @event keydown
58122          * The raw keydown event for the entire grid.
58123          * @param {Roo.EventObject} e
58124          */
58125         "keydown" : true,
58126
58127         // custom events
58128
58129         /**
58130          * @event cellclick
58131          * Fires when a cell is clicked
58132          * @param {Grid} this
58133          * @param {Number} rowIndex
58134          * @param {Number} columnIndex
58135          * @param {Roo.EventObject} e
58136          */
58137         "cellclick" : true,
58138         /**
58139          * @event celldblclick
58140          * Fires when a cell is double clicked
58141          * @param {Grid} this
58142          * @param {Number} rowIndex
58143          * @param {Number} columnIndex
58144          * @param {Roo.EventObject} e
58145          */
58146         "celldblclick" : true,
58147         /**
58148          * @event rowclick
58149          * Fires when a row is clicked
58150          * @param {Grid} this
58151          * @param {Number} rowIndex
58152          * @param {Roo.EventObject} e
58153          */
58154         "rowclick" : true,
58155         /**
58156          * @event rowdblclick
58157          * Fires when a row is double clicked
58158          * @param {Grid} this
58159          * @param {Number} rowIndex
58160          * @param {Roo.EventObject} e
58161          */
58162         "rowdblclick" : true,
58163         /**
58164          * @event headerclick
58165          * Fires when a header is clicked
58166          * @param {Grid} this
58167          * @param {Number} columnIndex
58168          * @param {Roo.EventObject} e
58169          */
58170         "headerclick" : true,
58171         /**
58172          * @event headerdblclick
58173          * Fires when a header cell is double clicked
58174          * @param {Grid} this
58175          * @param {Number} columnIndex
58176          * @param {Roo.EventObject} e
58177          */
58178         "headerdblclick" : true,
58179         /**
58180          * @event rowcontextmenu
58181          * Fires when a row is right clicked
58182          * @param {Grid} this
58183          * @param {Number} rowIndex
58184          * @param {Roo.EventObject} e
58185          */
58186         "rowcontextmenu" : true,
58187         /**
58188          * @event cellcontextmenu
58189          * Fires when a cell is right clicked
58190          * @param {Grid} this
58191          * @param {Number} rowIndex
58192          * @param {Number} cellIndex
58193          * @param {Roo.EventObject} e
58194          */
58195          "cellcontextmenu" : true,
58196         /**
58197          * @event headercontextmenu
58198          * Fires when a header is right clicked
58199          * @param {Grid} this
58200          * @param {Number} columnIndex
58201          * @param {Roo.EventObject} e
58202          */
58203         "headercontextmenu" : true,
58204         /**
58205          * @event bodyscroll
58206          * Fires when the body element is scrolled
58207          * @param {Number} scrollLeft
58208          * @param {Number} scrollTop
58209          */
58210         "bodyscroll" : true,
58211         /**
58212          * @event columnresize
58213          * Fires when the user resizes a column
58214          * @param {Number} columnIndex
58215          * @param {Number} newSize
58216          */
58217         "columnresize" : true,
58218         /**
58219          * @event columnmove
58220          * Fires when the user moves a column
58221          * @param {Number} oldIndex
58222          * @param {Number} newIndex
58223          */
58224         "columnmove" : true,
58225         /**
58226          * @event startdrag
58227          * Fires when row(s) start being dragged
58228          * @param {Grid} this
58229          * @param {Roo.GridDD} dd The drag drop object
58230          * @param {event} e The raw browser event
58231          */
58232         "startdrag" : true,
58233         /**
58234          * @event enddrag
58235          * Fires when a drag operation is complete
58236          * @param {Grid} this
58237          * @param {Roo.GridDD} dd The drag drop object
58238          * @param {event} e The raw browser event
58239          */
58240         "enddrag" : true,
58241         /**
58242          * @event dragdrop
58243          * Fires when dragged row(s) are dropped on a valid DD target
58244          * @param {Grid} this
58245          * @param {Roo.GridDD} dd The drag drop object
58246          * @param {String} targetId The target drag drop object
58247          * @param {event} e The raw browser event
58248          */
58249         "dragdrop" : true,
58250         /**
58251          * @event dragover
58252          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58253          * @param {Grid} this
58254          * @param {Roo.GridDD} dd The drag drop object
58255          * @param {String} targetId The target drag drop object
58256          * @param {event} e The raw browser event
58257          */
58258         "dragover" : true,
58259         /**
58260          * @event dragenter
58261          *  Fires when the dragged row(s) first cross another DD target while being dragged
58262          * @param {Grid} this
58263          * @param {Roo.GridDD} dd The drag drop object
58264          * @param {String} targetId The target drag drop object
58265          * @param {event} e The raw browser event
58266          */
58267         "dragenter" : true,
58268         /**
58269          * @event dragout
58270          * Fires when the dragged row(s) leave another DD target while being dragged
58271          * @param {Grid} this
58272          * @param {Roo.GridDD} dd The drag drop object
58273          * @param {String} targetId The target drag drop object
58274          * @param {event} e The raw browser event
58275          */
58276         "dragout" : true,
58277         /**
58278          * @event rowclass
58279          * Fires when a row is rendered, so you can change add a style to it.
58280          * @param {GridView} gridview   The grid view
58281          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58282          */
58283         'rowclass' : true,
58284
58285         /**
58286          * @event render
58287          * Fires when the grid is rendered
58288          * @param {Grid} grid
58289          */
58290         'render' : true
58291     });
58292
58293     Roo.grid.Grid.superclass.constructor.call(this);
58294 };
58295 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58296     
58297     /**
58298          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58299          */
58300         /**
58301          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58302          */
58303         /**
58304          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58305          */
58306         /**
58307          * @cfg {Roo.grid.Store} ds The data store for the grid
58308          */
58309         /**
58310          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58311          */
58312         /**
58313      * @cfg {String} ddGroup - drag drop group.
58314      */
58315       /**
58316      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58317      */
58318
58319     /**
58320      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58321      */
58322     minColumnWidth : 25,
58323
58324     /**
58325      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58326      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58327      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58328      */
58329     autoSizeColumns : false,
58330
58331     /**
58332      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58333      */
58334     autoSizeHeaders : true,
58335
58336     /**
58337      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58338      */
58339     monitorWindowResize : true,
58340
58341     /**
58342      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58343      * rows measured to get a columns size. Default is 0 (all rows).
58344      */
58345     maxRowsToMeasure : 0,
58346
58347     /**
58348      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58349      */
58350     trackMouseOver : true,
58351
58352     /**
58353     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58354     */
58355       /**
58356     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58357     */
58358     
58359     /**
58360     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58361     */
58362     enableDragDrop : false,
58363     
58364     /**
58365     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58366     */
58367     enableColumnMove : true,
58368     
58369     /**
58370     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58371     */
58372     enableColumnHide : true,
58373     
58374     /**
58375     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58376     */
58377     enableRowHeightSync : false,
58378     
58379     /**
58380     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58381     */
58382     stripeRows : true,
58383     
58384     /**
58385     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58386     */
58387     autoHeight : false,
58388
58389     /**
58390      * @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.
58391      */
58392     autoExpandColumn : false,
58393
58394     /**
58395     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58396     * Default is 50.
58397     */
58398     autoExpandMin : 50,
58399
58400     /**
58401     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58402     */
58403     autoExpandMax : 1000,
58404
58405     /**
58406     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58407     */
58408     view : null,
58409
58410     /**
58411     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58412     */
58413     loadMask : false,
58414     /**
58415     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58416     */
58417     dropTarget: false,
58418      /**
58419     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58420     */ 
58421     sortColMenu : false,
58422     
58423     // private
58424     rendered : false,
58425
58426     /**
58427     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58428     * of a fixed width. Default is false.
58429     */
58430     /**
58431     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58432     */
58433     
58434     
58435     /**
58436     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58437     * %0 is replaced with the number of selected rows.
58438     */
58439     ddText : "{0} selected row{1}",
58440     
58441     
58442     /**
58443      * Called once after all setup has been completed and the grid is ready to be rendered.
58444      * @return {Roo.grid.Grid} this
58445      */
58446     render : function()
58447     {
58448         var c = this.container;
58449         // try to detect autoHeight/width mode
58450         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58451             this.autoHeight = true;
58452         }
58453         var view = this.getView();
58454         view.init(this);
58455
58456         c.on("click", this.onClick, this);
58457         c.on("dblclick", this.onDblClick, this);
58458         c.on("contextmenu", this.onContextMenu, this);
58459         c.on("keydown", this.onKeyDown, this);
58460         if (Roo.isTouch) {
58461             c.on("touchstart", this.onTouchStart, this);
58462         }
58463
58464         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58465
58466         this.getSelectionModel().init(this);
58467
58468         view.render();
58469
58470         if(this.loadMask){
58471             this.loadMask = new Roo.LoadMask(this.container,
58472                     Roo.apply({store:this.dataSource}, this.loadMask));
58473         }
58474         
58475         
58476         if (this.toolbar && this.toolbar.xtype) {
58477             this.toolbar.container = this.getView().getHeaderPanel(true);
58478             this.toolbar = new Roo.Toolbar(this.toolbar);
58479         }
58480         if (this.footer && this.footer.xtype) {
58481             this.footer.dataSource = this.getDataSource();
58482             this.footer.container = this.getView().getFooterPanel(true);
58483             this.footer = Roo.factory(this.footer, Roo);
58484         }
58485         if (this.dropTarget && this.dropTarget.xtype) {
58486             delete this.dropTarget.xtype;
58487             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58488         }
58489         
58490         
58491         this.rendered = true;
58492         this.fireEvent('render', this);
58493         return this;
58494     },
58495
58496     /**
58497      * Reconfigures the grid to use a different Store and Column Model.
58498      * The View will be bound to the new objects and refreshed.
58499      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58500      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58501      */
58502     reconfigure : function(dataSource, colModel){
58503         if(this.loadMask){
58504             this.loadMask.destroy();
58505             this.loadMask = new Roo.LoadMask(this.container,
58506                     Roo.apply({store:dataSource}, this.loadMask));
58507         }
58508         this.view.bind(dataSource, colModel);
58509         this.dataSource = dataSource;
58510         this.colModel = colModel;
58511         this.view.refresh(true);
58512     },
58513     /**
58514      * addColumns
58515      * Add's a column, default at the end..
58516      
58517      * @param {int} position to add (default end)
58518      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58519      */
58520     addColumns : function(pos, ar)
58521     {
58522         
58523         for (var i =0;i< ar.length;i++) {
58524             var cfg = ar[i];
58525             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58526             this.cm.lookup[cfg.id] = cfg;
58527         }
58528         
58529         
58530         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58531             pos = this.cm.config.length; //this.cm.config.push(cfg);
58532         } 
58533         pos = Math.max(0,pos);
58534         ar.unshift(0);
58535         ar.unshift(pos);
58536         this.cm.config.splice.apply(this.cm.config, ar);
58537         
58538         
58539         
58540         this.view.generateRules(this.cm);
58541         this.view.refresh(true);
58542         
58543     },
58544     
58545     
58546     
58547     
58548     // private
58549     onKeyDown : function(e){
58550         this.fireEvent("keydown", e);
58551     },
58552
58553     /**
58554      * Destroy this grid.
58555      * @param {Boolean} removeEl True to remove the element
58556      */
58557     destroy : function(removeEl, keepListeners){
58558         if(this.loadMask){
58559             this.loadMask.destroy();
58560         }
58561         var c = this.container;
58562         c.removeAllListeners();
58563         this.view.destroy();
58564         this.colModel.purgeListeners();
58565         if(!keepListeners){
58566             this.purgeListeners();
58567         }
58568         c.update("");
58569         if(removeEl === true){
58570             c.remove();
58571         }
58572     },
58573
58574     // private
58575     processEvent : function(name, e){
58576         // does this fire select???
58577         //Roo.log('grid:processEvent '  + name);
58578         
58579         if (name != 'touchstart' ) {
58580             this.fireEvent(name, e);    
58581         }
58582         
58583         var t = e.getTarget();
58584         var v = this.view;
58585         var header = v.findHeaderIndex(t);
58586         if(header !== false){
58587             var ename = name == 'touchstart' ? 'click' : name;
58588              
58589             this.fireEvent("header" + ename, this, header, e);
58590         }else{
58591             var row = v.findRowIndex(t);
58592             var cell = v.findCellIndex(t);
58593             if (name == 'touchstart') {
58594                 // first touch is always a click.
58595                 // hopefull this happens after selection is updated.?
58596                 name = false;
58597                 
58598                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58599                     var cs = this.selModel.getSelectedCell();
58600                     if (row == cs[0] && cell == cs[1]){
58601                         name = 'dblclick';
58602                     }
58603                 }
58604                 if (typeof(this.selModel.getSelections) != 'undefined') {
58605                     var cs = this.selModel.getSelections();
58606                     var ds = this.dataSource;
58607                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58608                         name = 'dblclick';
58609                     }
58610                 }
58611                 if (!name) {
58612                     return;
58613                 }
58614             }
58615             
58616             
58617             if(row !== false){
58618                 this.fireEvent("row" + name, this, row, e);
58619                 if(cell !== false){
58620                     this.fireEvent("cell" + name, this, row, cell, e);
58621                 }
58622             }
58623         }
58624     },
58625
58626     // private
58627     onClick : function(e){
58628         this.processEvent("click", e);
58629     },
58630    // private
58631     onTouchStart : function(e){
58632         this.processEvent("touchstart", e);
58633     },
58634
58635     // private
58636     onContextMenu : function(e, t){
58637         this.processEvent("contextmenu", e);
58638     },
58639
58640     // private
58641     onDblClick : function(e){
58642         this.processEvent("dblclick", e);
58643     },
58644
58645     // private
58646     walkCells : function(row, col, step, fn, scope){
58647         var cm = this.colModel, clen = cm.getColumnCount();
58648         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58649         if(step < 0){
58650             if(col < 0){
58651                 row--;
58652                 first = false;
58653             }
58654             while(row >= 0){
58655                 if(!first){
58656                     col = clen-1;
58657                 }
58658                 first = false;
58659                 while(col >= 0){
58660                     if(fn.call(scope || this, row, col, cm) === true){
58661                         return [row, col];
58662                     }
58663                     col--;
58664                 }
58665                 row--;
58666             }
58667         } else {
58668             if(col >= clen){
58669                 row++;
58670                 first = false;
58671             }
58672             while(row < rlen){
58673                 if(!first){
58674                     col = 0;
58675                 }
58676                 first = false;
58677                 while(col < clen){
58678                     if(fn.call(scope || this, row, col, cm) === true){
58679                         return [row, col];
58680                     }
58681                     col++;
58682                 }
58683                 row++;
58684             }
58685         }
58686         return null;
58687     },
58688
58689     // private
58690     getSelections : function(){
58691         return this.selModel.getSelections();
58692     },
58693
58694     /**
58695      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58696      * but if manual update is required this method will initiate it.
58697      */
58698     autoSize : function(){
58699         if(this.rendered){
58700             this.view.layout();
58701             if(this.view.adjustForScroll){
58702                 this.view.adjustForScroll();
58703             }
58704         }
58705     },
58706
58707     /**
58708      * Returns the grid's underlying element.
58709      * @return {Element} The element
58710      */
58711     getGridEl : function(){
58712         return this.container;
58713     },
58714
58715     // private for compatibility, overridden by editor grid
58716     stopEditing : function(){},
58717
58718     /**
58719      * Returns the grid's SelectionModel.
58720      * @return {SelectionModel}
58721      */
58722     getSelectionModel : function(){
58723         if(!this.selModel){
58724             this.selModel = new Roo.grid.RowSelectionModel();
58725         }
58726         return this.selModel;
58727     },
58728
58729     /**
58730      * Returns the grid's DataSource.
58731      * @return {DataSource}
58732      */
58733     getDataSource : function(){
58734         return this.dataSource;
58735     },
58736
58737     /**
58738      * Returns the grid's ColumnModel.
58739      * @return {ColumnModel}
58740      */
58741     getColumnModel : function(){
58742         return this.colModel;
58743     },
58744
58745     /**
58746      * Returns the grid's GridView object.
58747      * @return {GridView}
58748      */
58749     getView : function(){
58750         if(!this.view){
58751             this.view = new Roo.grid.GridView(this.viewConfig);
58752             this.relayEvents(this.view, [
58753                 "beforerowremoved", "beforerowsinserted",
58754                 "beforerefresh", "rowremoved",
58755                 "rowsinserted", "rowupdated" ,"refresh"
58756             ]);
58757         }
58758         return this.view;
58759     },
58760     /**
58761      * Called to get grid's drag proxy text, by default returns this.ddText.
58762      * Override this to put something different in the dragged text.
58763      * @return {String}
58764      */
58765     getDragDropText : function(){
58766         var count = this.selModel.getCount();
58767         return String.format(this.ddText, count, count == 1 ? '' : 's');
58768     }
58769 });
58770 /*
58771  * Based on:
58772  * Ext JS Library 1.1.1
58773  * Copyright(c) 2006-2007, Ext JS, LLC.
58774  *
58775  * Originally Released Under LGPL - original licence link has changed is not relivant.
58776  *
58777  * Fork - LGPL
58778  * <script type="text/javascript">
58779  */
58780  /**
58781  * @class Roo.grid.AbstractGridView
58782  * @extends Roo.util.Observable
58783  * @abstract
58784  * Abstract base class for grid Views
58785  * @constructor
58786  */
58787 Roo.grid.AbstractGridView = function(){
58788         this.grid = null;
58789         
58790         this.events = {
58791             "beforerowremoved" : true,
58792             "beforerowsinserted" : true,
58793             "beforerefresh" : true,
58794             "rowremoved" : true,
58795             "rowsinserted" : true,
58796             "rowupdated" : true,
58797             "refresh" : true
58798         };
58799     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58800 };
58801
58802 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58803     rowClass : "x-grid-row",
58804     cellClass : "x-grid-cell",
58805     tdClass : "x-grid-td",
58806     hdClass : "x-grid-hd",
58807     splitClass : "x-grid-hd-split",
58808     
58809     init: function(grid){
58810         this.grid = grid;
58811                 var cid = this.grid.getGridEl().id;
58812         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58813         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58814         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58815         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58816         },
58817         
58818     getColumnRenderers : function(){
58819         var renderers = [];
58820         var cm = this.grid.colModel;
58821         var colCount = cm.getColumnCount();
58822         for(var i = 0; i < colCount; i++){
58823             renderers[i] = cm.getRenderer(i);
58824         }
58825         return renderers;
58826     },
58827     
58828     getColumnIds : function(){
58829         var ids = [];
58830         var cm = this.grid.colModel;
58831         var colCount = cm.getColumnCount();
58832         for(var i = 0; i < colCount; i++){
58833             ids[i] = cm.getColumnId(i);
58834         }
58835         return ids;
58836     },
58837     
58838     getDataIndexes : function(){
58839         if(!this.indexMap){
58840             this.indexMap = this.buildIndexMap();
58841         }
58842         return this.indexMap.colToData;
58843     },
58844     
58845     getColumnIndexByDataIndex : function(dataIndex){
58846         if(!this.indexMap){
58847             this.indexMap = this.buildIndexMap();
58848         }
58849         return this.indexMap.dataToCol[dataIndex];
58850     },
58851     
58852     /**
58853      * Set a css style for a column dynamically. 
58854      * @param {Number} colIndex The index of the column
58855      * @param {String} name The css property name
58856      * @param {String} value The css value
58857      */
58858     setCSSStyle : function(colIndex, name, value){
58859         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58860         Roo.util.CSS.updateRule(selector, name, value);
58861     },
58862     
58863     generateRules : function(cm){
58864         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58865         Roo.util.CSS.removeStyleSheet(rulesId);
58866         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58867             var cid = cm.getColumnId(i);
58868             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58869                          this.tdSelector, cid, " {\n}\n",
58870                          this.hdSelector, cid, " {\n}\n",
58871                          this.splitSelector, cid, " {\n}\n");
58872         }
58873         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58874     }
58875 });/*
58876  * Based on:
58877  * Ext JS Library 1.1.1
58878  * Copyright(c) 2006-2007, Ext JS, LLC.
58879  *
58880  * Originally Released Under LGPL - original licence link has changed is not relivant.
58881  *
58882  * Fork - LGPL
58883  * <script type="text/javascript">
58884  */
58885
58886 // private
58887 // This is a support class used internally by the Grid components
58888 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58889     this.grid = grid;
58890     this.view = grid.getView();
58891     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58892     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58893     if(hd2){
58894         this.setHandleElId(Roo.id(hd));
58895         this.setOuterHandleElId(Roo.id(hd2));
58896     }
58897     this.scroll = false;
58898 };
58899 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58900     maxDragWidth: 120,
58901     getDragData : function(e){
58902         var t = Roo.lib.Event.getTarget(e);
58903         var h = this.view.findHeaderCell(t);
58904         if(h){
58905             return {ddel: h.firstChild, header:h};
58906         }
58907         return false;
58908     },
58909
58910     onInitDrag : function(e){
58911         this.view.headersDisabled = true;
58912         var clone = this.dragData.ddel.cloneNode(true);
58913         clone.id = Roo.id();
58914         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58915         this.proxy.update(clone);
58916         return true;
58917     },
58918
58919     afterValidDrop : function(){
58920         var v = this.view;
58921         setTimeout(function(){
58922             v.headersDisabled = false;
58923         }, 50);
58924     },
58925
58926     afterInvalidDrop : function(){
58927         var v = this.view;
58928         setTimeout(function(){
58929             v.headersDisabled = false;
58930         }, 50);
58931     }
58932 });
58933 /*
58934  * Based on:
58935  * Ext JS Library 1.1.1
58936  * Copyright(c) 2006-2007, Ext JS, LLC.
58937  *
58938  * Originally Released Under LGPL - original licence link has changed is not relivant.
58939  *
58940  * Fork - LGPL
58941  * <script type="text/javascript">
58942  */
58943 // private
58944 // This is a support class used internally by the Grid components
58945 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58946     this.grid = grid;
58947     this.view = grid.getView();
58948     // split the proxies so they don't interfere with mouse events
58949     this.proxyTop = Roo.DomHelper.append(document.body, {
58950         cls:"col-move-top", html:"&#160;"
58951     }, true);
58952     this.proxyBottom = Roo.DomHelper.append(document.body, {
58953         cls:"col-move-bottom", html:"&#160;"
58954     }, true);
58955     this.proxyTop.hide = this.proxyBottom.hide = function(){
58956         this.setLeftTop(-100,-100);
58957         this.setStyle("visibility", "hidden");
58958     };
58959     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58960     // temporarily disabled
58961     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58962     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58963 };
58964 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58965     proxyOffsets : [-4, -9],
58966     fly: Roo.Element.fly,
58967
58968     getTargetFromEvent : function(e){
58969         var t = Roo.lib.Event.getTarget(e);
58970         var cindex = this.view.findCellIndex(t);
58971         if(cindex !== false){
58972             return this.view.getHeaderCell(cindex);
58973         }
58974         return null;
58975     },
58976
58977     nextVisible : function(h){
58978         var v = this.view, cm = this.grid.colModel;
58979         h = h.nextSibling;
58980         while(h){
58981             if(!cm.isHidden(v.getCellIndex(h))){
58982                 return h;
58983             }
58984             h = h.nextSibling;
58985         }
58986         return null;
58987     },
58988
58989     prevVisible : function(h){
58990         var v = this.view, cm = this.grid.colModel;
58991         h = h.prevSibling;
58992         while(h){
58993             if(!cm.isHidden(v.getCellIndex(h))){
58994                 return h;
58995             }
58996             h = h.prevSibling;
58997         }
58998         return null;
58999     },
59000
59001     positionIndicator : function(h, n, e){
59002         var x = Roo.lib.Event.getPageX(e);
59003         var r = Roo.lib.Dom.getRegion(n.firstChild);
59004         var px, pt, py = r.top + this.proxyOffsets[1];
59005         if((r.right - x) <= (r.right-r.left)/2){
59006             px = r.right+this.view.borderWidth;
59007             pt = "after";
59008         }else{
59009             px = r.left;
59010             pt = "before";
59011         }
59012         var oldIndex = this.view.getCellIndex(h);
59013         var newIndex = this.view.getCellIndex(n);
59014
59015         if(this.grid.colModel.isFixed(newIndex)){
59016             return false;
59017         }
59018
59019         var locked = this.grid.colModel.isLocked(newIndex);
59020
59021         if(pt == "after"){
59022             newIndex++;
59023         }
59024         if(oldIndex < newIndex){
59025             newIndex--;
59026         }
59027         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59028             return false;
59029         }
59030         px +=  this.proxyOffsets[0];
59031         this.proxyTop.setLeftTop(px, py);
59032         this.proxyTop.show();
59033         if(!this.bottomOffset){
59034             this.bottomOffset = this.view.mainHd.getHeight();
59035         }
59036         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59037         this.proxyBottom.show();
59038         return pt;
59039     },
59040
59041     onNodeEnter : function(n, dd, e, data){
59042         if(data.header != n){
59043             this.positionIndicator(data.header, n, e);
59044         }
59045     },
59046
59047     onNodeOver : function(n, dd, e, data){
59048         var result = false;
59049         if(data.header != n){
59050             result = this.positionIndicator(data.header, n, e);
59051         }
59052         if(!result){
59053             this.proxyTop.hide();
59054             this.proxyBottom.hide();
59055         }
59056         return result ? this.dropAllowed : this.dropNotAllowed;
59057     },
59058
59059     onNodeOut : function(n, dd, e, data){
59060         this.proxyTop.hide();
59061         this.proxyBottom.hide();
59062     },
59063
59064     onNodeDrop : function(n, dd, e, data){
59065         var h = data.header;
59066         if(h != n){
59067             var cm = this.grid.colModel;
59068             var x = Roo.lib.Event.getPageX(e);
59069             var r = Roo.lib.Dom.getRegion(n.firstChild);
59070             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59071             var oldIndex = this.view.getCellIndex(h);
59072             var newIndex = this.view.getCellIndex(n);
59073             var locked = cm.isLocked(newIndex);
59074             if(pt == "after"){
59075                 newIndex++;
59076             }
59077             if(oldIndex < newIndex){
59078                 newIndex--;
59079             }
59080             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59081                 return false;
59082             }
59083             cm.setLocked(oldIndex, locked, true);
59084             cm.moveColumn(oldIndex, newIndex);
59085             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59086             return true;
59087         }
59088         return false;
59089     }
59090 });
59091 /*
59092  * Based on:
59093  * Ext JS Library 1.1.1
59094  * Copyright(c) 2006-2007, Ext JS, LLC.
59095  *
59096  * Originally Released Under LGPL - original licence link has changed is not relivant.
59097  *
59098  * Fork - LGPL
59099  * <script type="text/javascript">
59100  */
59101   
59102 /**
59103  * @class Roo.grid.GridView
59104  * @extends Roo.util.Observable
59105  *
59106  * @constructor
59107  * @param {Object} config
59108  */
59109 Roo.grid.GridView = function(config){
59110     Roo.grid.GridView.superclass.constructor.call(this);
59111     this.el = null;
59112
59113     Roo.apply(this, config);
59114 };
59115
59116 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59117
59118     unselectable :  'unselectable="on"',
59119     unselectableCls :  'x-unselectable',
59120     
59121     
59122     rowClass : "x-grid-row",
59123
59124     cellClass : "x-grid-col",
59125
59126     tdClass : "x-grid-td",
59127
59128     hdClass : "x-grid-hd",
59129
59130     splitClass : "x-grid-split",
59131
59132     sortClasses : ["sort-asc", "sort-desc"],
59133
59134     enableMoveAnim : false,
59135
59136     hlColor: "C3DAF9",
59137
59138     dh : Roo.DomHelper,
59139
59140     fly : Roo.Element.fly,
59141
59142     css : Roo.util.CSS,
59143
59144     borderWidth: 1,
59145
59146     splitOffset: 3,
59147
59148     scrollIncrement : 22,
59149
59150     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59151
59152     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59153
59154     bind : function(ds, cm){
59155         if(this.ds){
59156             this.ds.un("load", this.onLoad, this);
59157             this.ds.un("datachanged", this.onDataChange, this);
59158             this.ds.un("add", this.onAdd, this);
59159             this.ds.un("remove", this.onRemove, this);
59160             this.ds.un("update", this.onUpdate, this);
59161             this.ds.un("clear", this.onClear, this);
59162         }
59163         if(ds){
59164             ds.on("load", this.onLoad, this);
59165             ds.on("datachanged", this.onDataChange, this);
59166             ds.on("add", this.onAdd, this);
59167             ds.on("remove", this.onRemove, this);
59168             ds.on("update", this.onUpdate, this);
59169             ds.on("clear", this.onClear, this);
59170         }
59171         this.ds = ds;
59172
59173         if(this.cm){
59174             this.cm.un("widthchange", this.onColWidthChange, this);
59175             this.cm.un("headerchange", this.onHeaderChange, this);
59176             this.cm.un("hiddenchange", this.onHiddenChange, this);
59177             this.cm.un("columnmoved", this.onColumnMove, this);
59178             this.cm.un("columnlockchange", this.onColumnLock, this);
59179         }
59180         if(cm){
59181             this.generateRules(cm);
59182             cm.on("widthchange", this.onColWidthChange, this);
59183             cm.on("headerchange", this.onHeaderChange, this);
59184             cm.on("hiddenchange", this.onHiddenChange, this);
59185             cm.on("columnmoved", this.onColumnMove, this);
59186             cm.on("columnlockchange", this.onColumnLock, this);
59187         }
59188         this.cm = cm;
59189     },
59190
59191     init: function(grid){
59192         Roo.grid.GridView.superclass.init.call(this, grid);
59193
59194         this.bind(grid.dataSource, grid.colModel);
59195
59196         grid.on("headerclick", this.handleHeaderClick, this);
59197
59198         if(grid.trackMouseOver){
59199             grid.on("mouseover", this.onRowOver, this);
59200             grid.on("mouseout", this.onRowOut, this);
59201         }
59202         grid.cancelTextSelection = function(){};
59203         this.gridId = grid.id;
59204
59205         var tpls = this.templates || {};
59206
59207         if(!tpls.master){
59208             tpls.master = new Roo.Template(
59209                '<div class="x-grid" hidefocus="true">',
59210                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59211                   '<div class="x-grid-topbar"></div>',
59212                   '<div class="x-grid-scroller"><div></div></div>',
59213                   '<div class="x-grid-locked">',
59214                       '<div class="x-grid-header">{lockedHeader}</div>',
59215                       '<div class="x-grid-body">{lockedBody}</div>',
59216                   "</div>",
59217                   '<div class="x-grid-viewport">',
59218                       '<div class="x-grid-header">{header}</div>',
59219                       '<div class="x-grid-body">{body}</div>',
59220                   "</div>",
59221                   '<div class="x-grid-bottombar"></div>',
59222                  
59223                   '<div class="x-grid-resize-proxy">&#160;</div>',
59224                "</div>"
59225             );
59226             tpls.master.disableformats = true;
59227         }
59228
59229         if(!tpls.header){
59230             tpls.header = new Roo.Template(
59231                '<table border="0" cellspacing="0" cellpadding="0">',
59232                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59233                "</table>{splits}"
59234             );
59235             tpls.header.disableformats = true;
59236         }
59237         tpls.header.compile();
59238
59239         if(!tpls.hcell){
59240             tpls.hcell = new Roo.Template(
59241                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59242                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59243                 "</div></td>"
59244              );
59245              tpls.hcell.disableFormats = true;
59246         }
59247         tpls.hcell.compile();
59248
59249         if(!tpls.hsplit){
59250             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59251                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59252             tpls.hsplit.disableFormats = true;
59253         }
59254         tpls.hsplit.compile();
59255
59256         if(!tpls.body){
59257             tpls.body = new Roo.Template(
59258                '<table border="0" cellspacing="0" cellpadding="0">',
59259                "<tbody>{rows}</tbody>",
59260                "</table>"
59261             );
59262             tpls.body.disableFormats = true;
59263         }
59264         tpls.body.compile();
59265
59266         if(!tpls.row){
59267             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59268             tpls.row.disableFormats = true;
59269         }
59270         tpls.row.compile();
59271
59272         if(!tpls.cell){
59273             tpls.cell = new Roo.Template(
59274                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59275                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59276                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59277                 "</td>"
59278             );
59279             tpls.cell.disableFormats = true;
59280         }
59281         tpls.cell.compile();
59282
59283         this.templates = tpls;
59284     },
59285
59286     // remap these for backwards compat
59287     onColWidthChange : function(){
59288         this.updateColumns.apply(this, arguments);
59289     },
59290     onHeaderChange : function(){
59291         this.updateHeaders.apply(this, arguments);
59292     }, 
59293     onHiddenChange : function(){
59294         this.handleHiddenChange.apply(this, arguments);
59295     },
59296     onColumnMove : function(){
59297         this.handleColumnMove.apply(this, arguments);
59298     },
59299     onColumnLock : function(){
59300         this.handleLockChange.apply(this, arguments);
59301     },
59302
59303     onDataChange : function(){
59304         this.refresh();
59305         this.updateHeaderSortState();
59306     },
59307
59308     onClear : function(){
59309         this.refresh();
59310     },
59311
59312     onUpdate : function(ds, record){
59313         this.refreshRow(record);
59314     },
59315
59316     refreshRow : function(record){
59317         var ds = this.ds, index;
59318         if(typeof record == 'number'){
59319             index = record;
59320             record = ds.getAt(index);
59321         }else{
59322             index = ds.indexOf(record);
59323         }
59324         this.insertRows(ds, index, index, true);
59325         this.onRemove(ds, record, index+1, true);
59326         this.syncRowHeights(index, index);
59327         this.layout();
59328         this.fireEvent("rowupdated", this, index, record);
59329     },
59330
59331     onAdd : function(ds, records, index){
59332         this.insertRows(ds, index, index + (records.length-1));
59333     },
59334
59335     onRemove : function(ds, record, index, isUpdate){
59336         if(isUpdate !== true){
59337             this.fireEvent("beforerowremoved", this, index, record);
59338         }
59339         var bt = this.getBodyTable(), lt = this.getLockedTable();
59340         if(bt.rows[index]){
59341             bt.firstChild.removeChild(bt.rows[index]);
59342         }
59343         if(lt.rows[index]){
59344             lt.firstChild.removeChild(lt.rows[index]);
59345         }
59346         if(isUpdate !== true){
59347             this.stripeRows(index);
59348             this.syncRowHeights(index, index);
59349             this.layout();
59350             this.fireEvent("rowremoved", this, index, record);
59351         }
59352     },
59353
59354     onLoad : function(){
59355         this.scrollToTop();
59356     },
59357
59358     /**
59359      * Scrolls the grid to the top
59360      */
59361     scrollToTop : function(){
59362         if(this.scroller){
59363             this.scroller.dom.scrollTop = 0;
59364             this.syncScroll();
59365         }
59366     },
59367
59368     /**
59369      * Gets a panel in the header of the grid that can be used for toolbars etc.
59370      * After modifying the contents of this panel a call to grid.autoSize() may be
59371      * required to register any changes in size.
59372      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59373      * @return Roo.Element
59374      */
59375     getHeaderPanel : function(doShow){
59376         if(doShow){
59377             this.headerPanel.show();
59378         }
59379         return this.headerPanel;
59380     },
59381
59382     /**
59383      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59384      * After modifying the contents of this panel a call to grid.autoSize() may be
59385      * required to register any changes in size.
59386      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59387      * @return Roo.Element
59388      */
59389     getFooterPanel : function(doShow){
59390         if(doShow){
59391             this.footerPanel.show();
59392         }
59393         return this.footerPanel;
59394     },
59395
59396     initElements : function(){
59397         var E = Roo.Element;
59398         var el = this.grid.getGridEl().dom.firstChild;
59399         var cs = el.childNodes;
59400
59401         this.el = new E(el);
59402         
59403          this.focusEl = new E(el.firstChild);
59404         this.focusEl.swallowEvent("click", true);
59405         
59406         this.headerPanel = new E(cs[1]);
59407         this.headerPanel.enableDisplayMode("block");
59408
59409         this.scroller = new E(cs[2]);
59410         this.scrollSizer = new E(this.scroller.dom.firstChild);
59411
59412         this.lockedWrap = new E(cs[3]);
59413         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59414         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59415
59416         this.mainWrap = new E(cs[4]);
59417         this.mainHd = new E(this.mainWrap.dom.firstChild);
59418         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59419
59420         this.footerPanel = new E(cs[5]);
59421         this.footerPanel.enableDisplayMode("block");
59422
59423         this.resizeProxy = new E(cs[6]);
59424
59425         this.headerSelector = String.format(
59426            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59427            this.lockedHd.id, this.mainHd.id
59428         );
59429
59430         this.splitterSelector = String.format(
59431            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59432            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59433         );
59434     },
59435     idToCssName : function(s)
59436     {
59437         return s.replace(/[^a-z0-9]+/ig, '-');
59438     },
59439
59440     getHeaderCell : function(index){
59441         return Roo.DomQuery.select(this.headerSelector)[index];
59442     },
59443
59444     getHeaderCellMeasure : function(index){
59445         return this.getHeaderCell(index).firstChild;
59446     },
59447
59448     getHeaderCellText : function(index){
59449         return this.getHeaderCell(index).firstChild.firstChild;
59450     },
59451
59452     getLockedTable : function(){
59453         return this.lockedBody.dom.firstChild;
59454     },
59455
59456     getBodyTable : function(){
59457         return this.mainBody.dom.firstChild;
59458     },
59459
59460     getLockedRow : function(index){
59461         return this.getLockedTable().rows[index];
59462     },
59463
59464     getRow : function(index){
59465         return this.getBodyTable().rows[index];
59466     },
59467
59468     getRowComposite : function(index){
59469         if(!this.rowEl){
59470             this.rowEl = new Roo.CompositeElementLite();
59471         }
59472         var els = [], lrow, mrow;
59473         if(lrow = this.getLockedRow(index)){
59474             els.push(lrow);
59475         }
59476         if(mrow = this.getRow(index)){
59477             els.push(mrow);
59478         }
59479         this.rowEl.elements = els;
59480         return this.rowEl;
59481     },
59482     /**
59483      * Gets the 'td' of the cell
59484      * 
59485      * @param {Integer} rowIndex row to select
59486      * @param {Integer} colIndex column to select
59487      * 
59488      * @return {Object} 
59489      */
59490     getCell : function(rowIndex, colIndex){
59491         var locked = this.cm.getLockedCount();
59492         var source;
59493         if(colIndex < locked){
59494             source = this.lockedBody.dom.firstChild;
59495         }else{
59496             source = this.mainBody.dom.firstChild;
59497             colIndex -= locked;
59498         }
59499         return source.rows[rowIndex].childNodes[colIndex];
59500     },
59501
59502     getCellText : function(rowIndex, colIndex){
59503         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59504     },
59505
59506     getCellBox : function(cell){
59507         var b = this.fly(cell).getBox();
59508         if(Roo.isOpera){ // opera fails to report the Y
59509             b.y = cell.offsetTop + this.mainBody.getY();
59510         }
59511         return b;
59512     },
59513
59514     getCellIndex : function(cell){
59515         var id = String(cell.className).match(this.cellRE);
59516         if(id){
59517             return parseInt(id[1], 10);
59518         }
59519         return 0;
59520     },
59521
59522     findHeaderIndex : function(n){
59523         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59524         return r ? this.getCellIndex(r) : false;
59525     },
59526
59527     findHeaderCell : function(n){
59528         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59529         return r ? r : false;
59530     },
59531
59532     findRowIndex : function(n){
59533         if(!n){
59534             return false;
59535         }
59536         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59537         return r ? r.rowIndex : false;
59538     },
59539
59540     findCellIndex : function(node){
59541         var stop = this.el.dom;
59542         while(node && node != stop){
59543             if(this.findRE.test(node.className)){
59544                 return this.getCellIndex(node);
59545             }
59546             node = node.parentNode;
59547         }
59548         return false;
59549     },
59550
59551     getColumnId : function(index){
59552         return this.cm.getColumnId(index);
59553     },
59554
59555     getSplitters : function()
59556     {
59557         if(this.splitterSelector){
59558            return Roo.DomQuery.select(this.splitterSelector);
59559         }else{
59560             return null;
59561       }
59562     },
59563
59564     getSplitter : function(index){
59565         return this.getSplitters()[index];
59566     },
59567
59568     onRowOver : function(e, t){
59569         var row;
59570         if((row = this.findRowIndex(t)) !== false){
59571             this.getRowComposite(row).addClass("x-grid-row-over");
59572         }
59573     },
59574
59575     onRowOut : function(e, t){
59576         var row;
59577         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59578             this.getRowComposite(row).removeClass("x-grid-row-over");
59579         }
59580     },
59581
59582     renderHeaders : function(){
59583         var cm = this.cm;
59584         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59585         var cb = [], lb = [], sb = [], lsb = [], p = {};
59586         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59587             p.cellId = "x-grid-hd-0-" + i;
59588             p.splitId = "x-grid-csplit-0-" + i;
59589             p.id = cm.getColumnId(i);
59590             p.value = cm.getColumnHeader(i) || "";
59591             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59592             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59593             if(!cm.isLocked(i)){
59594                 cb[cb.length] = ct.apply(p);
59595                 sb[sb.length] = st.apply(p);
59596             }else{
59597                 lb[lb.length] = ct.apply(p);
59598                 lsb[lsb.length] = st.apply(p);
59599             }
59600         }
59601         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59602                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59603     },
59604
59605     updateHeaders : function(){
59606         var html = this.renderHeaders();
59607         this.lockedHd.update(html[0]);
59608         this.mainHd.update(html[1]);
59609     },
59610
59611     /**
59612      * Focuses the specified row.
59613      * @param {Number} row The row index
59614      */
59615     focusRow : function(row)
59616     {
59617         //Roo.log('GridView.focusRow');
59618         var x = this.scroller.dom.scrollLeft;
59619         this.focusCell(row, 0, false);
59620         this.scroller.dom.scrollLeft = x;
59621     },
59622
59623     /**
59624      * Focuses the specified cell.
59625      * @param {Number} row The row index
59626      * @param {Number} col The column index
59627      * @param {Boolean} hscroll false to disable horizontal scrolling
59628      */
59629     focusCell : function(row, col, hscroll)
59630     {
59631         //Roo.log('GridView.focusCell');
59632         var el = this.ensureVisible(row, col, hscroll);
59633         this.focusEl.alignTo(el, "tl-tl");
59634         if(Roo.isGecko){
59635             this.focusEl.focus();
59636         }else{
59637             this.focusEl.focus.defer(1, this.focusEl);
59638         }
59639     },
59640
59641     /**
59642      * Scrolls the specified cell into view
59643      * @param {Number} row The row index
59644      * @param {Number} col The column index
59645      * @param {Boolean} hscroll false to disable horizontal scrolling
59646      */
59647     ensureVisible : function(row, col, hscroll)
59648     {
59649         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59650         //return null; //disable for testing.
59651         if(typeof row != "number"){
59652             row = row.rowIndex;
59653         }
59654         if(row < 0 && row >= this.ds.getCount()){
59655             return  null;
59656         }
59657         col = (col !== undefined ? col : 0);
59658         var cm = this.grid.colModel;
59659         while(cm.isHidden(col)){
59660             col++;
59661         }
59662
59663         var el = this.getCell(row, col);
59664         if(!el){
59665             return null;
59666         }
59667         var c = this.scroller.dom;
59668
59669         var ctop = parseInt(el.offsetTop, 10);
59670         var cleft = parseInt(el.offsetLeft, 10);
59671         var cbot = ctop + el.offsetHeight;
59672         var cright = cleft + el.offsetWidth;
59673         
59674         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59675         var stop = parseInt(c.scrollTop, 10);
59676         var sleft = parseInt(c.scrollLeft, 10);
59677         var sbot = stop + ch;
59678         var sright = sleft + c.clientWidth;
59679         /*
59680         Roo.log('GridView.ensureVisible:' +
59681                 ' ctop:' + ctop +
59682                 ' c.clientHeight:' + c.clientHeight +
59683                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59684                 ' stop:' + stop +
59685                 ' cbot:' + cbot +
59686                 ' sbot:' + sbot +
59687                 ' ch:' + ch  
59688                 );
59689         */
59690         if(ctop < stop){
59691             c.scrollTop = ctop;
59692             //Roo.log("set scrolltop to ctop DISABLE?");
59693         }else if(cbot > sbot){
59694             //Roo.log("set scrolltop to cbot-ch");
59695             c.scrollTop = cbot-ch;
59696         }
59697         
59698         if(hscroll !== false){
59699             if(cleft < sleft){
59700                 c.scrollLeft = cleft;
59701             }else if(cright > sright){
59702                 c.scrollLeft = cright-c.clientWidth;
59703             }
59704         }
59705          
59706         return el;
59707     },
59708
59709     updateColumns : function(){
59710         this.grid.stopEditing();
59711         var cm = this.grid.colModel, colIds = this.getColumnIds();
59712         //var totalWidth = cm.getTotalWidth();
59713         var pos = 0;
59714         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59715             //if(cm.isHidden(i)) continue;
59716             var w = cm.getColumnWidth(i);
59717             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59718             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59719         }
59720         this.updateSplitters();
59721     },
59722
59723     generateRules : function(cm){
59724         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59725         Roo.util.CSS.removeStyleSheet(rulesId);
59726         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59727             var cid = cm.getColumnId(i);
59728             var align = '';
59729             if(cm.config[i].align){
59730                 align = 'text-align:'+cm.config[i].align+';';
59731             }
59732             var hidden = '';
59733             if(cm.isHidden(i)){
59734                 hidden = 'display:none;';
59735             }
59736             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59737             ruleBuf.push(
59738                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59739                     this.hdSelector, cid, " {\n", align, width, "}\n",
59740                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59741                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59742         }
59743         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59744     },
59745
59746     updateSplitters : function(){
59747         var cm = this.cm, s = this.getSplitters();
59748         if(s){ // splitters not created yet
59749             var pos = 0, locked = true;
59750             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59751                 if(cm.isHidden(i)) {
59752                     continue;
59753                 }
59754                 var w = cm.getColumnWidth(i); // make sure it's a number
59755                 if(!cm.isLocked(i) && locked){
59756                     pos = 0;
59757                     locked = false;
59758                 }
59759                 pos += w;
59760                 s[i].style.left = (pos-this.splitOffset) + "px";
59761             }
59762         }
59763     },
59764
59765     handleHiddenChange : function(colModel, colIndex, hidden){
59766         if(hidden){
59767             this.hideColumn(colIndex);
59768         }else{
59769             this.unhideColumn(colIndex);
59770         }
59771     },
59772
59773     hideColumn : function(colIndex){
59774         var cid = this.getColumnId(colIndex);
59775         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59776         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59777         if(Roo.isSafari){
59778             this.updateHeaders();
59779         }
59780         this.updateSplitters();
59781         this.layout();
59782     },
59783
59784     unhideColumn : function(colIndex){
59785         var cid = this.getColumnId(colIndex);
59786         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59787         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59788
59789         if(Roo.isSafari){
59790             this.updateHeaders();
59791         }
59792         this.updateSplitters();
59793         this.layout();
59794     },
59795
59796     insertRows : function(dm, firstRow, lastRow, isUpdate){
59797         if(firstRow == 0 && lastRow == dm.getCount()-1){
59798             this.refresh();
59799         }else{
59800             if(!isUpdate){
59801                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59802             }
59803             var s = this.getScrollState();
59804             var markup = this.renderRows(firstRow, lastRow);
59805             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59806             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59807             this.restoreScroll(s);
59808             if(!isUpdate){
59809                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59810                 this.syncRowHeights(firstRow, lastRow);
59811                 this.stripeRows(firstRow);
59812                 this.layout();
59813             }
59814         }
59815     },
59816
59817     bufferRows : function(markup, target, index){
59818         var before = null, trows = target.rows, tbody = target.tBodies[0];
59819         if(index < trows.length){
59820             before = trows[index];
59821         }
59822         var b = document.createElement("div");
59823         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59824         var rows = b.firstChild.rows;
59825         for(var i = 0, len = rows.length; i < len; i++){
59826             if(before){
59827                 tbody.insertBefore(rows[0], before);
59828             }else{
59829                 tbody.appendChild(rows[0]);
59830             }
59831         }
59832         b.innerHTML = "";
59833         b = null;
59834     },
59835
59836     deleteRows : function(dm, firstRow, lastRow){
59837         if(dm.getRowCount()<1){
59838             this.fireEvent("beforerefresh", this);
59839             this.mainBody.update("");
59840             this.lockedBody.update("");
59841             this.fireEvent("refresh", this);
59842         }else{
59843             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59844             var bt = this.getBodyTable();
59845             var tbody = bt.firstChild;
59846             var rows = bt.rows;
59847             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59848                 tbody.removeChild(rows[firstRow]);
59849             }
59850             this.stripeRows(firstRow);
59851             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59852         }
59853     },
59854
59855     updateRows : function(dataSource, firstRow, lastRow){
59856         var s = this.getScrollState();
59857         this.refresh();
59858         this.restoreScroll(s);
59859     },
59860
59861     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59862         if(!noRefresh){
59863            this.refresh();
59864         }
59865         this.updateHeaderSortState();
59866     },
59867
59868     getScrollState : function(){
59869         
59870         var sb = this.scroller.dom;
59871         return {left: sb.scrollLeft, top: sb.scrollTop};
59872     },
59873
59874     stripeRows : function(startRow){
59875         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59876             return;
59877         }
59878         startRow = startRow || 0;
59879         var rows = this.getBodyTable().rows;
59880         var lrows = this.getLockedTable().rows;
59881         var cls = ' x-grid-row-alt ';
59882         for(var i = startRow, len = rows.length; i < len; i++){
59883             var row = rows[i], lrow = lrows[i];
59884             var isAlt = ((i+1) % 2 == 0);
59885             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59886             if(isAlt == hasAlt){
59887                 continue;
59888             }
59889             if(isAlt){
59890                 row.className += " x-grid-row-alt";
59891             }else{
59892                 row.className = row.className.replace("x-grid-row-alt", "");
59893             }
59894             if(lrow){
59895                 lrow.className = row.className;
59896             }
59897         }
59898     },
59899
59900     restoreScroll : function(state){
59901         //Roo.log('GridView.restoreScroll');
59902         var sb = this.scroller.dom;
59903         sb.scrollLeft = state.left;
59904         sb.scrollTop = state.top;
59905         this.syncScroll();
59906     },
59907
59908     syncScroll : function(){
59909         //Roo.log('GridView.syncScroll');
59910         var sb = this.scroller.dom;
59911         var sh = this.mainHd.dom;
59912         var bs = this.mainBody.dom;
59913         var lv = this.lockedBody.dom;
59914         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59915         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59916     },
59917
59918     handleScroll : function(e){
59919         this.syncScroll();
59920         var sb = this.scroller.dom;
59921         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59922         e.stopEvent();
59923     },
59924
59925     handleWheel : function(e){
59926         var d = e.getWheelDelta();
59927         this.scroller.dom.scrollTop -= d*22;
59928         // set this here to prevent jumpy scrolling on large tables
59929         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59930         e.stopEvent();
59931     },
59932
59933     renderRows : function(startRow, endRow){
59934         // pull in all the crap needed to render rows
59935         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59936         var colCount = cm.getColumnCount();
59937
59938         if(ds.getCount() < 1){
59939             return ["", ""];
59940         }
59941
59942         // build a map for all the columns
59943         var cs = [];
59944         for(var i = 0; i < colCount; i++){
59945             var name = cm.getDataIndex(i);
59946             cs[i] = {
59947                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59948                 renderer : cm.getRenderer(i),
59949                 id : cm.getColumnId(i),
59950                 locked : cm.isLocked(i),
59951                 has_editor : cm.isCellEditable(i)
59952             };
59953         }
59954
59955         startRow = startRow || 0;
59956         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59957
59958         // records to render
59959         var rs = ds.getRange(startRow, endRow);
59960
59961         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59962     },
59963
59964     // As much as I hate to duplicate code, this was branched because FireFox really hates
59965     // [].join("") on strings. The performance difference was substantial enough to
59966     // branch this function
59967     doRender : Roo.isGecko ?
59968             function(cs, rs, ds, startRow, colCount, stripe){
59969                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59970                 // buffers
59971                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59972                 
59973                 var hasListener = this.grid.hasListener('rowclass');
59974                 var rowcfg = {};
59975                 for(var j = 0, len = rs.length; j < len; j++){
59976                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59977                     for(var i = 0; i < colCount; i++){
59978                         c = cs[i];
59979                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59980                         p.id = c.id;
59981                         p.css = p.attr = "";
59982                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59983                         if(p.value == undefined || p.value === "") {
59984                             p.value = "&#160;";
59985                         }
59986                         if(c.has_editor){
59987                             p.css += ' x-grid-editable-cell';
59988                         }
59989                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59990                             p.css +=  ' x-grid-dirty-cell';
59991                         }
59992                         var markup = ct.apply(p);
59993                         if(!c.locked){
59994                             cb+= markup;
59995                         }else{
59996                             lcb+= markup;
59997                         }
59998                     }
59999                     var alt = [];
60000                     if(stripe && ((rowIndex+1) % 2 == 0)){
60001                         alt.push("x-grid-row-alt")
60002                     }
60003                     if(r.dirty){
60004                         alt.push(  " x-grid-dirty-row");
60005                     }
60006                     rp.cells = lcb;
60007                     if(this.getRowClass){
60008                         alt.push(this.getRowClass(r, rowIndex));
60009                     }
60010                     if (hasListener) {
60011                         rowcfg = {
60012                              
60013                             record: r,
60014                             rowIndex : rowIndex,
60015                             rowClass : ''
60016                         };
60017                         this.grid.fireEvent('rowclass', this, rowcfg);
60018                         alt.push(rowcfg.rowClass);
60019                     }
60020                     rp.alt = alt.join(" ");
60021                     lbuf+= rt.apply(rp);
60022                     rp.cells = cb;
60023                     buf+=  rt.apply(rp);
60024                 }
60025                 return [lbuf, buf];
60026             } :
60027             function(cs, rs, ds, startRow, colCount, stripe){
60028                 var ts = this.templates, ct = ts.cell, rt = ts.row;
60029                 // buffers
60030                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60031                 var hasListener = this.grid.hasListener('rowclass');
60032  
60033                 var rowcfg = {};
60034                 for(var j = 0, len = rs.length; j < len; j++){
60035                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60036                     for(var i = 0; i < colCount; i++){
60037                         c = cs[i];
60038                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60039                         p.id = c.id;
60040                         p.css = p.attr = "";
60041                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60042                         if(p.value == undefined || p.value === "") {
60043                             p.value = "&#160;";
60044                         }
60045                         //Roo.log(c);
60046                          if(c.has_editor){
60047                             p.css += ' x-grid-editable-cell';
60048                         }
60049                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60050                             p.css += ' x-grid-dirty-cell' 
60051                         }
60052                         
60053                         var markup = ct.apply(p);
60054                         if(!c.locked){
60055                             cb[cb.length] = markup;
60056                         }else{
60057                             lcb[lcb.length] = markup;
60058                         }
60059                     }
60060                     var alt = [];
60061                     if(stripe && ((rowIndex+1) % 2 == 0)){
60062                         alt.push( "x-grid-row-alt");
60063                     }
60064                     if(r.dirty){
60065                         alt.push(" x-grid-dirty-row");
60066                     }
60067                     rp.cells = lcb;
60068                     if(this.getRowClass){
60069                         alt.push( this.getRowClass(r, rowIndex));
60070                     }
60071                     if (hasListener) {
60072                         rowcfg = {
60073                              
60074                             record: r,
60075                             rowIndex : rowIndex,
60076                             rowClass : ''
60077                         };
60078                         this.grid.fireEvent('rowclass', this, rowcfg);
60079                         alt.push(rowcfg.rowClass);
60080                     }
60081                     
60082                     rp.alt = alt.join(" ");
60083                     rp.cells = lcb.join("");
60084                     lbuf[lbuf.length] = rt.apply(rp);
60085                     rp.cells = cb.join("");
60086                     buf[buf.length] =  rt.apply(rp);
60087                 }
60088                 return [lbuf.join(""), buf.join("")];
60089             },
60090
60091     renderBody : function(){
60092         var markup = this.renderRows();
60093         var bt = this.templates.body;
60094         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60095     },
60096
60097     /**
60098      * Refreshes the grid
60099      * @param {Boolean} headersToo
60100      */
60101     refresh : function(headersToo){
60102         this.fireEvent("beforerefresh", this);
60103         this.grid.stopEditing();
60104         var result = this.renderBody();
60105         this.lockedBody.update(result[0]);
60106         this.mainBody.update(result[1]);
60107         if(headersToo === true){
60108             this.updateHeaders();
60109             this.updateColumns();
60110             this.updateSplitters();
60111             this.updateHeaderSortState();
60112         }
60113         this.syncRowHeights();
60114         this.layout();
60115         this.fireEvent("refresh", this);
60116     },
60117
60118     handleColumnMove : function(cm, oldIndex, newIndex){
60119         this.indexMap = null;
60120         var s = this.getScrollState();
60121         this.refresh(true);
60122         this.restoreScroll(s);
60123         this.afterMove(newIndex);
60124     },
60125
60126     afterMove : function(colIndex){
60127         if(this.enableMoveAnim && Roo.enableFx){
60128             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60129         }
60130         // if multisort - fix sortOrder, and reload..
60131         if (this.grid.dataSource.multiSort) {
60132             // the we can call sort again..
60133             var dm = this.grid.dataSource;
60134             var cm = this.grid.colModel;
60135             var so = [];
60136             for(var i = 0; i < cm.config.length; i++ ) {
60137                 
60138                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60139                     continue; // dont' bother, it's not in sort list or being set.
60140                 }
60141                 
60142                 so.push(cm.config[i].dataIndex);
60143             };
60144             dm.sortOrder = so;
60145             dm.load(dm.lastOptions);
60146             
60147             
60148         }
60149         
60150     },
60151
60152     updateCell : function(dm, rowIndex, dataIndex){
60153         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60154         if(typeof colIndex == "undefined"){ // not present in grid
60155             return;
60156         }
60157         var cm = this.grid.colModel;
60158         var cell = this.getCell(rowIndex, colIndex);
60159         var cellText = this.getCellText(rowIndex, colIndex);
60160
60161         var p = {
60162             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60163             id : cm.getColumnId(colIndex),
60164             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60165         };
60166         var renderer = cm.getRenderer(colIndex);
60167         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60168         if(typeof val == "undefined" || val === "") {
60169             val = "&#160;";
60170         }
60171         cellText.innerHTML = val;
60172         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60173         this.syncRowHeights(rowIndex, rowIndex);
60174     },
60175
60176     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60177         var maxWidth = 0;
60178         if(this.grid.autoSizeHeaders){
60179             var h = this.getHeaderCellMeasure(colIndex);
60180             maxWidth = Math.max(maxWidth, h.scrollWidth);
60181         }
60182         var tb, index;
60183         if(this.cm.isLocked(colIndex)){
60184             tb = this.getLockedTable();
60185             index = colIndex;
60186         }else{
60187             tb = this.getBodyTable();
60188             index = colIndex - this.cm.getLockedCount();
60189         }
60190         if(tb && tb.rows){
60191             var rows = tb.rows;
60192             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60193             for(var i = 0; i < stopIndex; i++){
60194                 var cell = rows[i].childNodes[index].firstChild;
60195                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60196             }
60197         }
60198         return maxWidth + /*margin for error in IE*/ 5;
60199     },
60200     /**
60201      * Autofit a column to its content.
60202      * @param {Number} colIndex
60203      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60204      */
60205      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60206          if(this.cm.isHidden(colIndex)){
60207              return; // can't calc a hidden column
60208          }
60209         if(forceMinSize){
60210             var cid = this.cm.getColumnId(colIndex);
60211             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60212            if(this.grid.autoSizeHeaders){
60213                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60214            }
60215         }
60216         var newWidth = this.calcColumnWidth(colIndex);
60217         this.cm.setColumnWidth(colIndex,
60218             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60219         if(!suppressEvent){
60220             this.grid.fireEvent("columnresize", colIndex, newWidth);
60221         }
60222     },
60223
60224     /**
60225      * Autofits all columns to their content and then expands to fit any extra space in the grid
60226      */
60227      autoSizeColumns : function(){
60228         var cm = this.grid.colModel;
60229         var colCount = cm.getColumnCount();
60230         for(var i = 0; i < colCount; i++){
60231             this.autoSizeColumn(i, true, true);
60232         }
60233         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60234             this.fitColumns();
60235         }else{
60236             this.updateColumns();
60237             this.layout();
60238         }
60239     },
60240
60241     /**
60242      * Autofits all columns to the grid's width proportionate with their current size
60243      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60244      */
60245     fitColumns : function(reserveScrollSpace){
60246         var cm = this.grid.colModel;
60247         var colCount = cm.getColumnCount();
60248         var cols = [];
60249         var width = 0;
60250         var i, w;
60251         for (i = 0; i < colCount; i++){
60252             if(!cm.isHidden(i) && !cm.isFixed(i)){
60253                 w = cm.getColumnWidth(i);
60254                 cols.push(i);
60255                 cols.push(w);
60256                 width += w;
60257             }
60258         }
60259         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60260         if(reserveScrollSpace){
60261             avail -= 17;
60262         }
60263         var frac = (avail - cm.getTotalWidth())/width;
60264         while (cols.length){
60265             w = cols.pop();
60266             i = cols.pop();
60267             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60268         }
60269         this.updateColumns();
60270         this.layout();
60271     },
60272
60273     onRowSelect : function(rowIndex){
60274         var row = this.getRowComposite(rowIndex);
60275         row.addClass("x-grid-row-selected");
60276     },
60277
60278     onRowDeselect : function(rowIndex){
60279         var row = this.getRowComposite(rowIndex);
60280         row.removeClass("x-grid-row-selected");
60281     },
60282
60283     onCellSelect : function(row, col){
60284         var cell = this.getCell(row, col);
60285         if(cell){
60286             Roo.fly(cell).addClass("x-grid-cell-selected");
60287         }
60288     },
60289
60290     onCellDeselect : function(row, col){
60291         var cell = this.getCell(row, col);
60292         if(cell){
60293             Roo.fly(cell).removeClass("x-grid-cell-selected");
60294         }
60295     },
60296
60297     updateHeaderSortState : function(){
60298         
60299         // sort state can be single { field: xxx, direction : yyy}
60300         // or   { xxx=>ASC , yyy : DESC ..... }
60301         
60302         var mstate = {};
60303         if (!this.ds.multiSort) { 
60304             var state = this.ds.getSortState();
60305             if(!state){
60306                 return;
60307             }
60308             mstate[state.field] = state.direction;
60309             // FIXME... - this is not used here.. but might be elsewhere..
60310             this.sortState = state;
60311             
60312         } else {
60313             mstate = this.ds.sortToggle;
60314         }
60315         //remove existing sort classes..
60316         
60317         var sc = this.sortClasses;
60318         var hds = this.el.select(this.headerSelector).removeClass(sc);
60319         
60320         for(var f in mstate) {
60321         
60322             var sortColumn = this.cm.findColumnIndex(f);
60323             
60324             if(sortColumn != -1){
60325                 var sortDir = mstate[f];        
60326                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60327             }
60328         }
60329         
60330          
60331         
60332     },
60333
60334
60335     handleHeaderClick : function(g, index,e){
60336         
60337         Roo.log("header click");
60338         
60339         if (Roo.isTouch) {
60340             // touch events on header are handled by context
60341             this.handleHdCtx(g,index,e);
60342             return;
60343         }
60344         
60345         
60346         if(this.headersDisabled){
60347             return;
60348         }
60349         var dm = g.dataSource, cm = g.colModel;
60350         if(!cm.isSortable(index)){
60351             return;
60352         }
60353         g.stopEditing();
60354         
60355         if (dm.multiSort) {
60356             // update the sortOrder
60357             var so = [];
60358             for(var i = 0; i < cm.config.length; i++ ) {
60359                 
60360                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60361                     continue; // dont' bother, it's not in sort list or being set.
60362                 }
60363                 
60364                 so.push(cm.config[i].dataIndex);
60365             };
60366             dm.sortOrder = so;
60367         }
60368         
60369         
60370         dm.sort(cm.getDataIndex(index));
60371     },
60372
60373
60374     destroy : function(){
60375         if(this.colMenu){
60376             this.colMenu.removeAll();
60377             Roo.menu.MenuMgr.unregister(this.colMenu);
60378             this.colMenu.getEl().remove();
60379             delete this.colMenu;
60380         }
60381         if(this.hmenu){
60382             this.hmenu.removeAll();
60383             Roo.menu.MenuMgr.unregister(this.hmenu);
60384             this.hmenu.getEl().remove();
60385             delete this.hmenu;
60386         }
60387         if(this.grid.enableColumnMove){
60388             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60389             if(dds){
60390                 for(var dd in dds){
60391                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60392                         var elid = dds[dd].dragElId;
60393                         dds[dd].unreg();
60394                         Roo.get(elid).remove();
60395                     } else if(dds[dd].config.isTarget){
60396                         dds[dd].proxyTop.remove();
60397                         dds[dd].proxyBottom.remove();
60398                         dds[dd].unreg();
60399                     }
60400                     if(Roo.dd.DDM.locationCache[dd]){
60401                         delete Roo.dd.DDM.locationCache[dd];
60402                     }
60403                 }
60404                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60405             }
60406         }
60407         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60408         this.bind(null, null);
60409         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60410     },
60411
60412     handleLockChange : function(){
60413         this.refresh(true);
60414     },
60415
60416     onDenyColumnLock : function(){
60417
60418     },
60419
60420     onDenyColumnHide : function(){
60421
60422     },
60423
60424     handleHdMenuClick : function(item){
60425         var index = this.hdCtxIndex;
60426         var cm = this.cm, ds = this.ds;
60427         switch(item.id){
60428             case "asc":
60429                 ds.sort(cm.getDataIndex(index), "ASC");
60430                 break;
60431             case "desc":
60432                 ds.sort(cm.getDataIndex(index), "DESC");
60433                 break;
60434             case "lock":
60435                 var lc = cm.getLockedCount();
60436                 if(cm.getColumnCount(true) <= lc+1){
60437                     this.onDenyColumnLock();
60438                     return;
60439                 }
60440                 if(lc != index){
60441                     cm.setLocked(index, true, true);
60442                     cm.moveColumn(index, lc);
60443                     this.grid.fireEvent("columnmove", index, lc);
60444                 }else{
60445                     cm.setLocked(index, true);
60446                 }
60447             break;
60448             case "unlock":
60449                 var lc = cm.getLockedCount();
60450                 if((lc-1) != index){
60451                     cm.setLocked(index, false, true);
60452                     cm.moveColumn(index, lc-1);
60453                     this.grid.fireEvent("columnmove", index, lc-1);
60454                 }else{
60455                     cm.setLocked(index, false);
60456                 }
60457             break;
60458             case 'wider': // used to expand cols on touch..
60459             case 'narrow':
60460                 var cw = cm.getColumnWidth(index);
60461                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60462                 cw = Math.max(0, cw);
60463                 cw = Math.min(cw,4000);
60464                 cm.setColumnWidth(index, cw);
60465                 break;
60466                 
60467             default:
60468                 index = cm.getIndexById(item.id.substr(4));
60469                 if(index != -1){
60470                     if(item.checked && cm.getColumnCount(true) <= 1){
60471                         this.onDenyColumnHide();
60472                         return false;
60473                     }
60474                     cm.setHidden(index, item.checked);
60475                 }
60476         }
60477         return true;
60478     },
60479
60480     beforeColMenuShow : function(){
60481         var cm = this.cm,  colCount = cm.getColumnCount();
60482         this.colMenu.removeAll();
60483         
60484         var items = [];
60485         for(var i = 0; i < colCount; i++){
60486             items.push({
60487                 id: "col-"+cm.getColumnId(i),
60488                 text: cm.getColumnHeader(i),
60489                 checked: !cm.isHidden(i),
60490                 hideOnClick:false
60491             });
60492         }
60493         
60494         if (this.grid.sortColMenu) {
60495             items.sort(function(a,b) {
60496                 if (a.text == b.text) {
60497                     return 0;
60498                 }
60499                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60500             });
60501         }
60502         
60503         for(var i = 0; i < colCount; i++){
60504             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60505         }
60506     },
60507
60508     handleHdCtx : function(g, index, e){
60509         e.stopEvent();
60510         var hd = this.getHeaderCell(index);
60511         this.hdCtxIndex = index;
60512         var ms = this.hmenu.items, cm = this.cm;
60513         ms.get("asc").setDisabled(!cm.isSortable(index));
60514         ms.get("desc").setDisabled(!cm.isSortable(index));
60515         if(this.grid.enableColLock !== false){
60516             ms.get("lock").setDisabled(cm.isLocked(index));
60517             ms.get("unlock").setDisabled(!cm.isLocked(index));
60518         }
60519         this.hmenu.show(hd, "tl-bl");
60520     },
60521
60522     handleHdOver : function(e){
60523         var hd = this.findHeaderCell(e.getTarget());
60524         if(hd && !this.headersDisabled){
60525             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60526                this.fly(hd).addClass("x-grid-hd-over");
60527             }
60528         }
60529     },
60530
60531     handleHdOut : function(e){
60532         var hd = this.findHeaderCell(e.getTarget());
60533         if(hd){
60534             this.fly(hd).removeClass("x-grid-hd-over");
60535         }
60536     },
60537
60538     handleSplitDblClick : function(e, t){
60539         var i = this.getCellIndex(t);
60540         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60541             this.autoSizeColumn(i, true);
60542             this.layout();
60543         }
60544     },
60545
60546     render : function(){
60547
60548         var cm = this.cm;
60549         var colCount = cm.getColumnCount();
60550
60551         if(this.grid.monitorWindowResize === true){
60552             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60553         }
60554         var header = this.renderHeaders();
60555         var body = this.templates.body.apply({rows:""});
60556         var html = this.templates.master.apply({
60557             lockedBody: body,
60558             body: body,
60559             lockedHeader: header[0],
60560             header: header[1]
60561         });
60562
60563         //this.updateColumns();
60564
60565         this.grid.getGridEl().dom.innerHTML = html;
60566
60567         this.initElements();
60568         
60569         // a kludge to fix the random scolling effect in webkit
60570         this.el.on("scroll", function() {
60571             this.el.dom.scrollTop=0; // hopefully not recursive..
60572         },this);
60573
60574         this.scroller.on("scroll", this.handleScroll, this);
60575         this.lockedBody.on("mousewheel", this.handleWheel, this);
60576         this.mainBody.on("mousewheel", this.handleWheel, this);
60577
60578         this.mainHd.on("mouseover", this.handleHdOver, this);
60579         this.mainHd.on("mouseout", this.handleHdOut, this);
60580         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60581                 {delegate: "."+this.splitClass});
60582
60583         this.lockedHd.on("mouseover", this.handleHdOver, this);
60584         this.lockedHd.on("mouseout", this.handleHdOut, this);
60585         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60586                 {delegate: "."+this.splitClass});
60587
60588         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60589             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60590         }
60591
60592         this.updateSplitters();
60593
60594         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60595             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60596             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60597         }
60598
60599         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60600             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60601             this.hmenu.add(
60602                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60603                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60604             );
60605             if(this.grid.enableColLock !== false){
60606                 this.hmenu.add('-',
60607                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60608                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60609                 );
60610             }
60611             if (Roo.isTouch) {
60612                  this.hmenu.add('-',
60613                     {id:"wider", text: this.columnsWiderText},
60614                     {id:"narrow", text: this.columnsNarrowText }
60615                 );
60616                 
60617                  
60618             }
60619             
60620             if(this.grid.enableColumnHide !== false){
60621
60622                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60623                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60624                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60625
60626                 this.hmenu.add('-',
60627                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60628                 );
60629             }
60630             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60631
60632             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60633         }
60634
60635         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60636             this.dd = new Roo.grid.GridDragZone(this.grid, {
60637                 ddGroup : this.grid.ddGroup || 'GridDD'
60638             });
60639             
60640         }
60641
60642         /*
60643         for(var i = 0; i < colCount; i++){
60644             if(cm.isHidden(i)){
60645                 this.hideColumn(i);
60646             }
60647             if(cm.config[i].align){
60648                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60649                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60650             }
60651         }*/
60652         
60653         this.updateHeaderSortState();
60654
60655         this.beforeInitialResize();
60656         this.layout(true);
60657
60658         // two part rendering gives faster view to the user
60659         this.renderPhase2.defer(1, this);
60660     },
60661
60662     renderPhase2 : function(){
60663         // render the rows now
60664         this.refresh();
60665         if(this.grid.autoSizeColumns){
60666             this.autoSizeColumns();
60667         }
60668     },
60669
60670     beforeInitialResize : function(){
60671
60672     },
60673
60674     onColumnSplitterMoved : function(i, w){
60675         this.userResized = true;
60676         var cm = this.grid.colModel;
60677         cm.setColumnWidth(i, w, true);
60678         var cid = cm.getColumnId(i);
60679         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60680         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60681         this.updateSplitters();
60682         this.layout();
60683         this.grid.fireEvent("columnresize", i, w);
60684     },
60685
60686     syncRowHeights : function(startIndex, endIndex){
60687         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60688             startIndex = startIndex || 0;
60689             var mrows = this.getBodyTable().rows;
60690             var lrows = this.getLockedTable().rows;
60691             var len = mrows.length-1;
60692             endIndex = Math.min(endIndex || len, len);
60693             for(var i = startIndex; i <= endIndex; i++){
60694                 var m = mrows[i], l = lrows[i];
60695                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60696                 m.style.height = l.style.height = h + "px";
60697             }
60698         }
60699     },
60700
60701     layout : function(initialRender, is2ndPass)
60702     {
60703         var g = this.grid;
60704         var auto = g.autoHeight;
60705         var scrollOffset = 16;
60706         var c = g.getGridEl(), cm = this.cm,
60707                 expandCol = g.autoExpandColumn,
60708                 gv = this;
60709         //c.beginMeasure();
60710
60711         if(!c.dom.offsetWidth){ // display:none?
60712             if(initialRender){
60713                 this.lockedWrap.show();
60714                 this.mainWrap.show();
60715             }
60716             return;
60717         }
60718
60719         var hasLock = this.cm.isLocked(0);
60720
60721         var tbh = this.headerPanel.getHeight();
60722         var bbh = this.footerPanel.getHeight();
60723
60724         if(auto){
60725             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60726             var newHeight = ch + c.getBorderWidth("tb");
60727             if(g.maxHeight){
60728                 newHeight = Math.min(g.maxHeight, newHeight);
60729             }
60730             c.setHeight(newHeight);
60731         }
60732
60733         if(g.autoWidth){
60734             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60735         }
60736
60737         var s = this.scroller;
60738
60739         var csize = c.getSize(true);
60740
60741         this.el.setSize(csize.width, csize.height);
60742
60743         this.headerPanel.setWidth(csize.width);
60744         this.footerPanel.setWidth(csize.width);
60745
60746         var hdHeight = this.mainHd.getHeight();
60747         var vw = csize.width;
60748         var vh = csize.height - (tbh + bbh);
60749
60750         s.setSize(vw, vh);
60751
60752         var bt = this.getBodyTable();
60753         
60754         if(cm.getLockedCount() == cm.config.length){
60755             bt = this.getLockedTable();
60756         }
60757         
60758         var ltWidth = hasLock ?
60759                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60760
60761         var scrollHeight = bt.offsetHeight;
60762         var scrollWidth = ltWidth + bt.offsetWidth;
60763         var vscroll = false, hscroll = false;
60764
60765         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60766
60767         var lw = this.lockedWrap, mw = this.mainWrap;
60768         var lb = this.lockedBody, mb = this.mainBody;
60769
60770         setTimeout(function(){
60771             var t = s.dom.offsetTop;
60772             var w = s.dom.clientWidth,
60773                 h = s.dom.clientHeight;
60774
60775             lw.setTop(t);
60776             lw.setSize(ltWidth, h);
60777
60778             mw.setLeftTop(ltWidth, t);
60779             mw.setSize(w-ltWidth, h);
60780
60781             lb.setHeight(h-hdHeight);
60782             mb.setHeight(h-hdHeight);
60783
60784             if(is2ndPass !== true && !gv.userResized && expandCol){
60785                 // high speed resize without full column calculation
60786                 
60787                 var ci = cm.getIndexById(expandCol);
60788                 if (ci < 0) {
60789                     ci = cm.findColumnIndex(expandCol);
60790                 }
60791                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60792                 var expandId = cm.getColumnId(ci);
60793                 var  tw = cm.getTotalWidth(false);
60794                 var currentWidth = cm.getColumnWidth(ci);
60795                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60796                 if(currentWidth != cw){
60797                     cm.setColumnWidth(ci, cw, true);
60798                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60799                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60800                     gv.updateSplitters();
60801                     gv.layout(false, true);
60802                 }
60803             }
60804
60805             if(initialRender){
60806                 lw.show();
60807                 mw.show();
60808             }
60809             //c.endMeasure();
60810         }, 10);
60811     },
60812
60813     onWindowResize : function(){
60814         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60815             return;
60816         }
60817         this.layout();
60818     },
60819
60820     appendFooter : function(parentEl){
60821         return null;
60822     },
60823
60824     sortAscText : "Sort Ascending",
60825     sortDescText : "Sort Descending",
60826     lockText : "Lock Column",
60827     unlockText : "Unlock Column",
60828     columnsText : "Columns",
60829  
60830     columnsWiderText : "Wider",
60831     columnsNarrowText : "Thinner"
60832 });
60833
60834
60835 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60836     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60837     this.proxy.el.addClass('x-grid3-col-dd');
60838 };
60839
60840 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60841     handleMouseDown : function(e){
60842
60843     },
60844
60845     callHandleMouseDown : function(e){
60846         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60847     }
60848 });
60849 /*
60850  * Based on:
60851  * Ext JS Library 1.1.1
60852  * Copyright(c) 2006-2007, Ext JS, LLC.
60853  *
60854  * Originally Released Under LGPL - original licence link has changed is not relivant.
60855  *
60856  * Fork - LGPL
60857  * <script type="text/javascript">
60858  */
60859  /**
60860  * @extends Roo.dd.DDProxy
60861  * @class Roo.grid.SplitDragZone
60862  * Support for Column Header resizing
60863  * @constructor
60864  * @param {Object} config
60865  */
60866 // private
60867 // This is a support class used internally by the Grid components
60868 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60869     this.grid = grid;
60870     this.view = grid.getView();
60871     this.proxy = this.view.resizeProxy;
60872     Roo.grid.SplitDragZone.superclass.constructor.call(
60873         this,
60874         hd, // ID
60875         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60876         {  // CONFIG
60877             dragElId : Roo.id(this.proxy.dom),
60878             resizeFrame:false
60879         }
60880     );
60881     
60882     this.setHandleElId(Roo.id(hd));
60883     if (hd2 !== false) {
60884         this.setOuterHandleElId(Roo.id(hd2));
60885     }
60886     
60887     this.scroll = false;
60888 };
60889 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60890     fly: Roo.Element.fly,
60891
60892     b4StartDrag : function(x, y){
60893         this.view.headersDisabled = true;
60894         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60895                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60896         );
60897         this.proxy.setHeight(h);
60898         
60899         // for old system colWidth really stored the actual width?
60900         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60901         // which in reality did not work.. - it worked only for fixed sizes
60902         // for resizable we need to use actual sizes.
60903         var w = this.cm.getColumnWidth(this.cellIndex);
60904         if (!this.view.mainWrap) {
60905             // bootstrap.
60906             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60907         }
60908         
60909         
60910         
60911         // this was w-this.grid.minColumnWidth;
60912         // doesnt really make sense? - w = thie curren width or the rendered one?
60913         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60914         this.resetConstraints();
60915         this.setXConstraint(minw, 1000);
60916         this.setYConstraint(0, 0);
60917         this.minX = x - minw;
60918         this.maxX = x + 1000;
60919         this.startPos = x;
60920         if (!this.view.mainWrap) { // this is Bootstrap code..
60921             this.getDragEl().style.display='block';
60922         }
60923         
60924         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60925     },
60926
60927
60928     handleMouseDown : function(e){
60929         ev = Roo.EventObject.setEvent(e);
60930         var t = this.fly(ev.getTarget());
60931         if(t.hasClass("x-grid-split")){
60932             this.cellIndex = this.view.getCellIndex(t.dom);
60933             this.split = t.dom;
60934             this.cm = this.grid.colModel;
60935             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60936                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60937             }
60938         }
60939     },
60940
60941     endDrag : function(e){
60942         this.view.headersDisabled = false;
60943         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60944         var diff = endX - this.startPos;
60945         // 
60946         var w = this.cm.getColumnWidth(this.cellIndex);
60947         if (!this.view.mainWrap) {
60948             w = 0;
60949         }
60950         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60951     },
60952
60953     autoOffset : function(){
60954         this.setDelta(0,0);
60955     }
60956 });/*
60957  * Based on:
60958  * Ext JS Library 1.1.1
60959  * Copyright(c) 2006-2007, Ext JS, LLC.
60960  *
60961  * Originally Released Under LGPL - original licence link has changed is not relivant.
60962  *
60963  * Fork - LGPL
60964  * <script type="text/javascript">
60965  */
60966  
60967 // private
60968 // This is a support class used internally by the Grid components
60969 Roo.grid.GridDragZone = function(grid, config){
60970     this.view = grid.getView();
60971     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60972     if(this.view.lockedBody){
60973         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60974         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60975     }
60976     this.scroll = false;
60977     this.grid = grid;
60978     this.ddel = document.createElement('div');
60979     this.ddel.className = 'x-grid-dd-wrap';
60980 };
60981
60982 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60983     ddGroup : "GridDD",
60984
60985     getDragData : function(e){
60986         var t = Roo.lib.Event.getTarget(e);
60987         var rowIndex = this.view.findRowIndex(t);
60988         var sm = this.grid.selModel;
60989             
60990         //Roo.log(rowIndex);
60991         
60992         if (sm.getSelectedCell) {
60993             // cell selection..
60994             if (!sm.getSelectedCell()) {
60995                 return false;
60996             }
60997             if (rowIndex != sm.getSelectedCell()[0]) {
60998                 return false;
60999             }
61000         
61001         }
61002         if (sm.getSelections && sm.getSelections().length < 1) {
61003             return false;
61004         }
61005         
61006         
61007         // before it used to all dragging of unseleted... - now we dont do that.
61008         if(rowIndex !== false){
61009             
61010             // if editorgrid.. 
61011             
61012             
61013             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
61014                
61015             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
61016               //  
61017             //}
61018             if (e.hasModifier()){
61019                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
61020             }
61021             
61022             Roo.log("getDragData");
61023             
61024             return {
61025                 grid: this.grid,
61026                 ddel: this.ddel,
61027                 rowIndex: rowIndex,
61028                 selections: sm.getSelections ? sm.getSelections() : (
61029                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61030             };
61031         }
61032         return false;
61033     },
61034     
61035     
61036     onInitDrag : function(e){
61037         var data = this.dragData;
61038         this.ddel.innerHTML = this.grid.getDragDropText();
61039         this.proxy.update(this.ddel);
61040         // fire start drag?
61041     },
61042
61043     afterRepair : function(){
61044         this.dragging = false;
61045     },
61046
61047     getRepairXY : function(e, data){
61048         return false;
61049     },
61050
61051     onEndDrag : function(data, e){
61052         // fire end drag?
61053     },
61054
61055     onValidDrop : function(dd, e, id){
61056         // fire drag drop?
61057         this.hideProxy();
61058     },
61059
61060     beforeInvalidDrop : function(e, id){
61061
61062     }
61063 });/*
61064  * Based on:
61065  * Ext JS Library 1.1.1
61066  * Copyright(c) 2006-2007, Ext JS, LLC.
61067  *
61068  * Originally Released Under LGPL - original licence link has changed is not relivant.
61069  *
61070  * Fork - LGPL
61071  * <script type="text/javascript">
61072  */
61073  
61074
61075 /**
61076  * @class Roo.grid.ColumnModel
61077  * @extends Roo.util.Observable
61078  * This is the default implementation of a ColumnModel used by the Grid. It defines
61079  * the columns in the grid.
61080  * <br>Usage:<br>
61081  <pre><code>
61082  var colModel = new Roo.grid.ColumnModel([
61083         {header: "Ticker", width: 60, sortable: true, locked: true},
61084         {header: "Company Name", width: 150, sortable: true},
61085         {header: "Market Cap.", width: 100, sortable: true},
61086         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61087         {header: "Employees", width: 100, sortable: true, resizable: false}
61088  ]);
61089  </code></pre>
61090  * <p>
61091  
61092  * The config options listed for this class are options which may appear in each
61093  * individual column definition.
61094  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61095  * @constructor
61096  * @param {Object} config An Array of column config objects. See this class's
61097  * config objects for details.
61098 */
61099 Roo.grid.ColumnModel = function(config){
61100         /**
61101      * The config passed into the constructor
61102      */
61103     this.config = []; //config;
61104     this.lookup = {};
61105
61106     // if no id, create one
61107     // if the column does not have a dataIndex mapping,
61108     // map it to the order it is in the config
61109     for(var i = 0, len = config.length; i < len; i++){
61110         this.addColumn(config[i]);
61111         
61112     }
61113
61114     /**
61115      * The width of columns which have no width specified (defaults to 100)
61116      * @type Number
61117      */
61118     this.defaultWidth = 100;
61119
61120     /**
61121      * Default sortable of columns which have no sortable specified (defaults to false)
61122      * @type Boolean
61123      */
61124     this.defaultSortable = false;
61125
61126     this.addEvents({
61127         /**
61128              * @event widthchange
61129              * Fires when the width of a column changes.
61130              * @param {ColumnModel} this
61131              * @param {Number} columnIndex The column index
61132              * @param {Number} newWidth The new width
61133              */
61134             "widthchange": true,
61135         /**
61136              * @event headerchange
61137              * Fires when the text of a header changes.
61138              * @param {ColumnModel} this
61139              * @param {Number} columnIndex The column index
61140              * @param {Number} newText The new header text
61141              */
61142             "headerchange": true,
61143         /**
61144              * @event hiddenchange
61145              * Fires when a column is hidden or "unhidden".
61146              * @param {ColumnModel} this
61147              * @param {Number} columnIndex The column index
61148              * @param {Boolean} hidden true if hidden, false otherwise
61149              */
61150             "hiddenchange": true,
61151             /**
61152          * @event columnmoved
61153          * Fires when a column is moved.
61154          * @param {ColumnModel} this
61155          * @param {Number} oldIndex
61156          * @param {Number} newIndex
61157          */
61158         "columnmoved" : true,
61159         /**
61160          * @event columlockchange
61161          * Fires when a column's locked state is changed
61162          * @param {ColumnModel} this
61163          * @param {Number} colIndex
61164          * @param {Boolean} locked true if locked
61165          */
61166         "columnlockchange" : true
61167     });
61168     Roo.grid.ColumnModel.superclass.constructor.call(this);
61169 };
61170 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61171     /**
61172      * @cfg {String} header The header text to display in the Grid view.
61173      */
61174         /**
61175      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61176      */
61177         /**
61178      * @cfg {String} smHeader Header at Bootsrap Small width
61179      */
61180         /**
61181      * @cfg {String} mdHeader Header at Bootsrap Medium width
61182      */
61183         /**
61184      * @cfg {String} lgHeader Header at Bootsrap Large width
61185      */
61186         /**
61187      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61188      */
61189     /**
61190      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61191      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61192      * specified, the column's index is used as an index into the Record's data Array.
61193      */
61194     /**
61195      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61196      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61197      */
61198     /**
61199      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61200      * Defaults to the value of the {@link #defaultSortable} property.
61201      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61202      */
61203     /**
61204      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61205      */
61206     /**
61207      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61208      */
61209     /**
61210      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61211      */
61212     /**
61213      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61214      */
61215     /**
61216      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61217      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61218      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61219      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61220      */
61221        /**
61222      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61223      */
61224     /**
61225      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61226      */
61227     /**
61228      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61229      */
61230     /**
61231      * @cfg {String} cursor (Optional)
61232      */
61233     /**
61234      * @cfg {String} tooltip (Optional)
61235      */
61236     /**
61237      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61238      */
61239     /**
61240      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61241      */
61242     /**
61243      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61244      */
61245     /**
61246      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61247      */
61248         /**
61249      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61250      */
61251     /**
61252      * Returns the id of the column at the specified index.
61253      * @param {Number} index The column index
61254      * @return {String} the id
61255      */
61256     getColumnId : function(index){
61257         return this.config[index].id;
61258     },
61259
61260     /**
61261      * Returns the column for a specified id.
61262      * @param {String} id The column id
61263      * @return {Object} the column
61264      */
61265     getColumnById : function(id){
61266         return this.lookup[id];
61267     },
61268
61269     
61270     /**
61271      * Returns the column Object for a specified dataIndex.
61272      * @param {String} dataIndex The column dataIndex
61273      * @return {Object|Boolean} the column or false if not found
61274      */
61275     getColumnByDataIndex: function(dataIndex){
61276         var index = this.findColumnIndex(dataIndex);
61277         return index > -1 ? this.config[index] : false;
61278     },
61279     
61280     /**
61281      * Returns the index for a specified column id.
61282      * @param {String} id The column id
61283      * @return {Number} the index, or -1 if not found
61284      */
61285     getIndexById : function(id){
61286         for(var i = 0, len = this.config.length; i < len; i++){
61287             if(this.config[i].id == id){
61288                 return i;
61289             }
61290         }
61291         return -1;
61292     },
61293     
61294     /**
61295      * Returns the index for a specified column dataIndex.
61296      * @param {String} dataIndex The column dataIndex
61297      * @return {Number} the index, or -1 if not found
61298      */
61299     
61300     findColumnIndex : function(dataIndex){
61301         for(var i = 0, len = this.config.length; i < len; i++){
61302             if(this.config[i].dataIndex == dataIndex){
61303                 return i;
61304             }
61305         }
61306         return -1;
61307     },
61308     
61309     
61310     moveColumn : function(oldIndex, newIndex){
61311         var c = this.config[oldIndex];
61312         this.config.splice(oldIndex, 1);
61313         this.config.splice(newIndex, 0, c);
61314         this.dataMap = null;
61315         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61316     },
61317
61318     isLocked : function(colIndex){
61319         return this.config[colIndex].locked === true;
61320     },
61321
61322     setLocked : function(colIndex, value, suppressEvent){
61323         if(this.isLocked(colIndex) == value){
61324             return;
61325         }
61326         this.config[colIndex].locked = value;
61327         if(!suppressEvent){
61328             this.fireEvent("columnlockchange", this, colIndex, value);
61329         }
61330     },
61331
61332     getTotalLockedWidth : function(){
61333         var totalWidth = 0;
61334         for(var i = 0; i < this.config.length; i++){
61335             if(this.isLocked(i) && !this.isHidden(i)){
61336                 this.totalWidth += this.getColumnWidth(i);
61337             }
61338         }
61339         return totalWidth;
61340     },
61341
61342     getLockedCount : function(){
61343         for(var i = 0, len = this.config.length; i < len; i++){
61344             if(!this.isLocked(i)){
61345                 return i;
61346             }
61347         }
61348         
61349         return this.config.length;
61350     },
61351
61352     /**
61353      * Returns the number of columns.
61354      * @return {Number}
61355      */
61356     getColumnCount : function(visibleOnly){
61357         if(visibleOnly === true){
61358             var c = 0;
61359             for(var i = 0, len = this.config.length; i < len; i++){
61360                 if(!this.isHidden(i)){
61361                     c++;
61362                 }
61363             }
61364             return c;
61365         }
61366         return this.config.length;
61367     },
61368
61369     /**
61370      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61371      * @param {Function} fn
61372      * @param {Object} scope (optional)
61373      * @return {Array} result
61374      */
61375     getColumnsBy : function(fn, scope){
61376         var r = [];
61377         for(var i = 0, len = this.config.length; i < len; i++){
61378             var c = this.config[i];
61379             if(fn.call(scope||this, c, i) === true){
61380                 r[r.length] = c;
61381             }
61382         }
61383         return r;
61384     },
61385
61386     /**
61387      * Returns true if the specified column is sortable.
61388      * @param {Number} col The column index
61389      * @return {Boolean}
61390      */
61391     isSortable : function(col){
61392         if(typeof this.config[col].sortable == "undefined"){
61393             return this.defaultSortable;
61394         }
61395         return this.config[col].sortable;
61396     },
61397
61398     /**
61399      * Returns the rendering (formatting) function defined for the column.
61400      * @param {Number} col The column index.
61401      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61402      */
61403     getRenderer : function(col){
61404         if(!this.config[col].renderer){
61405             return Roo.grid.ColumnModel.defaultRenderer;
61406         }
61407         return this.config[col].renderer;
61408     },
61409
61410     /**
61411      * Sets the rendering (formatting) function for a column.
61412      * @param {Number} col The column index
61413      * @param {Function} fn The function to use to process the cell's raw data
61414      * to return HTML markup for the grid view. The render function is called with
61415      * the following parameters:<ul>
61416      * <li>Data value.</li>
61417      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61418      * <li>css A CSS style string to apply to the table cell.</li>
61419      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61420      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61421      * <li>Row index</li>
61422      * <li>Column index</li>
61423      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61424      */
61425     setRenderer : function(col, fn){
61426         this.config[col].renderer = fn;
61427     },
61428
61429     /**
61430      * Returns the width for the specified column.
61431      * @param {Number} col The column index
61432      * @param (optional) {String} gridSize bootstrap width size.
61433      * @return {Number}
61434      */
61435     getColumnWidth : function(col, gridSize)
61436         {
61437                 var cfg = this.config[col];
61438                 
61439                 if (typeof(gridSize) == 'undefined') {
61440                         return cfg.width * 1 || this.defaultWidth;
61441                 }
61442                 if (gridSize === false) { // if we set it..
61443                         return cfg.width || false;
61444                 }
61445                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61446                 
61447                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61448                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61449                                 continue;
61450                         }
61451                         return cfg[ sizes[i] ];
61452                 }
61453                 return 1;
61454                 
61455     },
61456
61457     /**
61458      * Sets the width for a column.
61459      * @param {Number} col The column index
61460      * @param {Number} width The new width
61461      */
61462     setColumnWidth : function(col, width, suppressEvent){
61463         this.config[col].width = width;
61464         this.totalWidth = null;
61465         if(!suppressEvent){
61466              this.fireEvent("widthchange", this, col, width);
61467         }
61468     },
61469
61470     /**
61471      * Returns the total width of all columns.
61472      * @param {Boolean} includeHidden True to include hidden column widths
61473      * @return {Number}
61474      */
61475     getTotalWidth : function(includeHidden){
61476         if(!this.totalWidth){
61477             this.totalWidth = 0;
61478             for(var i = 0, len = this.config.length; i < len; i++){
61479                 if(includeHidden || !this.isHidden(i)){
61480                     this.totalWidth += this.getColumnWidth(i);
61481                 }
61482             }
61483         }
61484         return this.totalWidth;
61485     },
61486
61487     /**
61488      * Returns the header for the specified column.
61489      * @param {Number} col The column index
61490      * @return {String}
61491      */
61492     getColumnHeader : function(col){
61493         return this.config[col].header;
61494     },
61495
61496     /**
61497      * Sets the header for a column.
61498      * @param {Number} col The column index
61499      * @param {String} header The new header
61500      */
61501     setColumnHeader : function(col, header){
61502         this.config[col].header = header;
61503         this.fireEvent("headerchange", this, col, header);
61504     },
61505
61506     /**
61507      * Returns the tooltip for the specified column.
61508      * @param {Number} col The column index
61509      * @return {String}
61510      */
61511     getColumnTooltip : function(col){
61512             return this.config[col].tooltip;
61513     },
61514     /**
61515      * Sets the tooltip for a column.
61516      * @param {Number} col The column index
61517      * @param {String} tooltip The new tooltip
61518      */
61519     setColumnTooltip : function(col, tooltip){
61520             this.config[col].tooltip = tooltip;
61521     },
61522
61523     /**
61524      * Returns the dataIndex for the specified column.
61525      * @param {Number} col The column index
61526      * @return {Number}
61527      */
61528     getDataIndex : function(col){
61529         return this.config[col].dataIndex;
61530     },
61531
61532     /**
61533      * Sets the dataIndex for a column.
61534      * @param {Number} col The column index
61535      * @param {Number} dataIndex The new dataIndex
61536      */
61537     setDataIndex : function(col, dataIndex){
61538         this.config[col].dataIndex = dataIndex;
61539     },
61540
61541     
61542     
61543     /**
61544      * Returns true if the cell is editable.
61545      * @param {Number} colIndex The column index
61546      * @param {Number} rowIndex The row index - this is nto actually used..?
61547      * @return {Boolean}
61548      */
61549     isCellEditable : function(colIndex, rowIndex){
61550         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61551     },
61552
61553     /**
61554      * Returns the editor defined for the cell/column.
61555      * return false or null to disable editing.
61556      * @param {Number} colIndex The column index
61557      * @param {Number} rowIndex The row index
61558      * @return {Object}
61559      */
61560     getCellEditor : function(colIndex, rowIndex){
61561         return this.config[colIndex].editor;
61562     },
61563
61564     /**
61565      * Sets if a column is editable.
61566      * @param {Number} col The column index
61567      * @param {Boolean} editable True if the column is editable
61568      */
61569     setEditable : function(col, editable){
61570         this.config[col].editable = editable;
61571     },
61572
61573
61574     /**
61575      * Returns true if the column is hidden.
61576      * @param {Number} colIndex The column index
61577      * @return {Boolean}
61578      */
61579     isHidden : function(colIndex){
61580         return this.config[colIndex].hidden;
61581     },
61582
61583
61584     /**
61585      * Returns true if the column width cannot be changed
61586      */
61587     isFixed : function(colIndex){
61588         return this.config[colIndex].fixed;
61589     },
61590
61591     /**
61592      * Returns true if the column can be resized
61593      * @return {Boolean}
61594      */
61595     isResizable : function(colIndex){
61596         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61597     },
61598     /**
61599      * Sets if a column is hidden.
61600      * @param {Number} colIndex The column index
61601      * @param {Boolean} hidden True if the column is hidden
61602      */
61603     setHidden : function(colIndex, hidden){
61604         this.config[colIndex].hidden = hidden;
61605         this.totalWidth = null;
61606         this.fireEvent("hiddenchange", this, colIndex, hidden);
61607     },
61608
61609     /**
61610      * Sets the editor for a column.
61611      * @param {Number} col The column index
61612      * @param {Object} editor The editor object
61613      */
61614     setEditor : function(col, editor){
61615         this.config[col].editor = editor;
61616     },
61617     /**
61618      * Add a column (experimental...) - defaults to adding to the end..
61619      * @param {Object} config 
61620     */
61621     addColumn : function(c)
61622     {
61623     
61624         var i = this.config.length;
61625         this.config[i] = c;
61626         
61627         if(typeof c.dataIndex == "undefined"){
61628             c.dataIndex = i;
61629         }
61630         if(typeof c.renderer == "string"){
61631             c.renderer = Roo.util.Format[c.renderer];
61632         }
61633         if(typeof c.id == "undefined"){
61634             c.id = Roo.id();
61635         }
61636         if(c.editor && c.editor.xtype){
61637             c.editor  = Roo.factory(c.editor, Roo.grid);
61638         }
61639         if(c.editor && c.editor.isFormField){
61640             c.editor = new Roo.grid.GridEditor(c.editor);
61641         }
61642         this.lookup[c.id] = c;
61643     }
61644     
61645 });
61646
61647 Roo.grid.ColumnModel.defaultRenderer = function(value)
61648 {
61649     if(typeof value == "object") {
61650         return value;
61651     }
61652         if(typeof value == "string" && value.length < 1){
61653             return "&#160;";
61654         }
61655     
61656         return String.format("{0}", value);
61657 };
61658
61659 // Alias for backwards compatibility
61660 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61661 /*
61662  * Based on:
61663  * Ext JS Library 1.1.1
61664  * Copyright(c) 2006-2007, Ext JS, LLC.
61665  *
61666  * Originally Released Under LGPL - original licence link has changed is not relivant.
61667  *
61668  * Fork - LGPL
61669  * <script type="text/javascript">
61670  */
61671
61672 /**
61673  * @class Roo.grid.AbstractSelectionModel
61674  * @extends Roo.util.Observable
61675  * @abstract
61676  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61677  * implemented by descendant classes.  This class should not be directly instantiated.
61678  * @constructor
61679  */
61680 Roo.grid.AbstractSelectionModel = function(){
61681     this.locked = false;
61682     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61683 };
61684
61685 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61686     /** @ignore Called by the grid automatically. Do not call directly. */
61687     init : function(grid){
61688         this.grid = grid;
61689         this.initEvents();
61690     },
61691
61692     /**
61693      * Locks the selections.
61694      */
61695     lock : function(){
61696         this.locked = true;
61697     },
61698
61699     /**
61700      * Unlocks the selections.
61701      */
61702     unlock : function(){
61703         this.locked = false;
61704     },
61705
61706     /**
61707      * Returns true if the selections are locked.
61708      * @return {Boolean}
61709      */
61710     isLocked : function(){
61711         return this.locked;
61712     }
61713 });/*
61714  * Based on:
61715  * Ext JS Library 1.1.1
61716  * Copyright(c) 2006-2007, Ext JS, LLC.
61717  *
61718  * Originally Released Under LGPL - original licence link has changed is not relivant.
61719  *
61720  * Fork - LGPL
61721  * <script type="text/javascript">
61722  */
61723 /**
61724  * @extends Roo.grid.AbstractSelectionModel
61725  * @class Roo.grid.RowSelectionModel
61726  * The default SelectionModel used by {@link Roo.grid.Grid}.
61727  * It supports multiple selections and keyboard selection/navigation. 
61728  * @constructor
61729  * @param {Object} config
61730  */
61731 Roo.grid.RowSelectionModel = function(config){
61732     Roo.apply(this, config);
61733     this.selections = new Roo.util.MixedCollection(false, function(o){
61734         return o.id;
61735     });
61736
61737     this.last = false;
61738     this.lastActive = false;
61739
61740     this.addEvents({
61741         /**
61742         * @event selectionchange
61743         * Fires when the selection changes
61744         * @param {SelectionModel} this
61745         */
61746        "selectionchange" : true,
61747        /**
61748         * @event afterselectionchange
61749         * Fires after the selection changes (eg. by key press or clicking)
61750         * @param {SelectionModel} this
61751         */
61752        "afterselectionchange" : true,
61753        /**
61754         * @event beforerowselect
61755         * Fires when a row is selected being selected, return false to cancel.
61756         * @param {SelectionModel} this
61757         * @param {Number} rowIndex The selected index
61758         * @param {Boolean} keepExisting False if other selections will be cleared
61759         */
61760        "beforerowselect" : true,
61761        /**
61762         * @event rowselect
61763         * Fires when a row is selected.
61764         * @param {SelectionModel} this
61765         * @param {Number} rowIndex The selected index
61766         * @param {Roo.data.Record} r The record
61767         */
61768        "rowselect" : true,
61769        /**
61770         * @event rowdeselect
61771         * Fires when a row is deselected.
61772         * @param {SelectionModel} this
61773         * @param {Number} rowIndex The selected index
61774         */
61775         "rowdeselect" : true
61776     });
61777     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61778     this.locked = false;
61779 };
61780
61781 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61782     /**
61783      * @cfg {Boolean} singleSelect
61784      * True to allow selection of only one row at a time (defaults to false)
61785      */
61786     singleSelect : false,
61787
61788     // private
61789     initEvents : function(){
61790
61791         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61792             this.grid.on("mousedown", this.handleMouseDown, this);
61793         }else{ // allow click to work like normal
61794             this.grid.on("rowclick", this.handleDragableRowClick, this);
61795         }
61796         // bootstrap does not have a view..
61797         var view = this.grid.view ? this.grid.view : this.grid;
61798         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61799             "up" : function(e){
61800                 if(!e.shiftKey){
61801                     this.selectPrevious(e.shiftKey);
61802                 }else if(this.last !== false && this.lastActive !== false){
61803                     var last = this.last;
61804                     this.selectRange(this.last,  this.lastActive-1);
61805                     view.focusRow(this.lastActive);
61806                     if(last !== false){
61807                         this.last = last;
61808                     }
61809                 }else{
61810                     this.selectFirstRow();
61811                 }
61812                 this.fireEvent("afterselectionchange", this);
61813             },
61814             "down" : function(e){
61815                 if(!e.shiftKey){
61816                     this.selectNext(e.shiftKey);
61817                 }else if(this.last !== false && this.lastActive !== false){
61818                     var last = this.last;
61819                     this.selectRange(this.last,  this.lastActive+1);
61820                     view.focusRow(this.lastActive);
61821                     if(last !== false){
61822                         this.last = last;
61823                     }
61824                 }else{
61825                     this.selectFirstRow();
61826                 }
61827                 this.fireEvent("afterselectionchange", this);
61828             },
61829             scope: this
61830         });
61831
61832          
61833         view.on("refresh", this.onRefresh, this);
61834         view.on("rowupdated", this.onRowUpdated, this);
61835         view.on("rowremoved", this.onRemove, this);
61836     },
61837
61838     // private
61839     onRefresh : function(){
61840         var ds = this.grid.ds, i, v = this.grid.view;
61841         var s = this.selections;
61842         s.each(function(r){
61843             if((i = ds.indexOfId(r.id)) != -1){
61844                 v.onRowSelect(i);
61845                 s.add(ds.getAt(i)); // updating the selection relate data
61846             }else{
61847                 s.remove(r);
61848             }
61849         });
61850     },
61851
61852     // private
61853     onRemove : function(v, index, r){
61854         this.selections.remove(r);
61855     },
61856
61857     // private
61858     onRowUpdated : function(v, index, r){
61859         if(this.isSelected(r)){
61860             v.onRowSelect(index);
61861         }
61862     },
61863
61864     /**
61865      * Select records.
61866      * @param {Array} records The records to select
61867      * @param {Boolean} keepExisting (optional) True to keep existing selections
61868      */
61869     selectRecords : function(records, keepExisting){
61870         if(!keepExisting){
61871             this.clearSelections();
61872         }
61873         var ds = this.grid.ds;
61874         for(var i = 0, len = records.length; i < len; i++){
61875             this.selectRow(ds.indexOf(records[i]), true);
61876         }
61877     },
61878
61879     /**
61880      * Gets the number of selected rows.
61881      * @return {Number}
61882      */
61883     getCount : function(){
61884         return this.selections.length;
61885     },
61886
61887     /**
61888      * Selects the first row in the grid.
61889      */
61890     selectFirstRow : function(){
61891         this.selectRow(0);
61892     },
61893
61894     /**
61895      * Select the last row.
61896      * @param {Boolean} keepExisting (optional) True to keep existing selections
61897      */
61898     selectLastRow : function(keepExisting){
61899         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61900     },
61901
61902     /**
61903      * Selects the row immediately following the last selected row.
61904      * @param {Boolean} keepExisting (optional) True to keep existing selections
61905      */
61906     selectNext : function(keepExisting){
61907         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61908             this.selectRow(this.last+1, keepExisting);
61909             var view = this.grid.view ? this.grid.view : this.grid;
61910             view.focusRow(this.last);
61911         }
61912     },
61913
61914     /**
61915      * Selects the row that precedes the last selected row.
61916      * @param {Boolean} keepExisting (optional) True to keep existing selections
61917      */
61918     selectPrevious : function(keepExisting){
61919         if(this.last){
61920             this.selectRow(this.last-1, keepExisting);
61921             var view = this.grid.view ? this.grid.view : this.grid;
61922             view.focusRow(this.last);
61923         }
61924     },
61925
61926     /**
61927      * Returns the selected records
61928      * @return {Array} Array of selected records
61929      */
61930     getSelections : function(){
61931         return [].concat(this.selections.items);
61932     },
61933
61934     /**
61935      * Returns the first selected record.
61936      * @return {Record}
61937      */
61938     getSelected : function(){
61939         return this.selections.itemAt(0);
61940     },
61941
61942
61943     /**
61944      * Clears all selections.
61945      */
61946     clearSelections : function(fast){
61947         if(this.locked) {
61948             return;
61949         }
61950         if(fast !== true){
61951             var ds = this.grid.ds;
61952             var s = this.selections;
61953             s.each(function(r){
61954                 this.deselectRow(ds.indexOfId(r.id));
61955             }, this);
61956             s.clear();
61957         }else{
61958             this.selections.clear();
61959         }
61960         this.last = false;
61961     },
61962
61963
61964     /**
61965      * Selects all rows.
61966      */
61967     selectAll : function(){
61968         if(this.locked) {
61969             return;
61970         }
61971         this.selections.clear();
61972         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61973             this.selectRow(i, true);
61974         }
61975     },
61976
61977     /**
61978      * Returns True if there is a selection.
61979      * @return {Boolean}
61980      */
61981     hasSelection : function(){
61982         return this.selections.length > 0;
61983     },
61984
61985     /**
61986      * Returns True if the specified row is selected.
61987      * @param {Number/Record} record The record or index of the record to check
61988      * @return {Boolean}
61989      */
61990     isSelected : function(index){
61991         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61992         return (r && this.selections.key(r.id) ? true : false);
61993     },
61994
61995     /**
61996      * Returns True if the specified record id is selected.
61997      * @param {String} id The id of record to check
61998      * @return {Boolean}
61999      */
62000     isIdSelected : function(id){
62001         return (this.selections.key(id) ? true : false);
62002     },
62003
62004     // private
62005     handleMouseDown : function(e, t)
62006     {
62007         var view = this.grid.view ? this.grid.view : this.grid;
62008         var rowIndex;
62009         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
62010             return;
62011         };
62012         if(e.shiftKey && this.last !== false){
62013             var last = this.last;
62014             this.selectRange(last, rowIndex, e.ctrlKey);
62015             this.last = last; // reset the last
62016             view.focusRow(rowIndex);
62017         }else{
62018             var isSelected = this.isSelected(rowIndex);
62019             if(e.button !== 0 && isSelected){
62020                 view.focusRow(rowIndex);
62021             }else if(e.ctrlKey && isSelected){
62022                 this.deselectRow(rowIndex);
62023             }else if(!isSelected){
62024                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
62025                 view.focusRow(rowIndex);
62026             }
62027         }
62028         this.fireEvent("afterselectionchange", this);
62029     },
62030     // private
62031     handleDragableRowClick :  function(grid, rowIndex, e) 
62032     {
62033         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62034             this.selectRow(rowIndex, false);
62035             var view = this.grid.view ? this.grid.view : this.grid;
62036             view.focusRow(rowIndex);
62037              this.fireEvent("afterselectionchange", this);
62038         }
62039     },
62040     
62041     /**
62042      * Selects multiple rows.
62043      * @param {Array} rows Array of the indexes of the row to select
62044      * @param {Boolean} keepExisting (optional) True to keep existing selections
62045      */
62046     selectRows : function(rows, keepExisting){
62047         if(!keepExisting){
62048             this.clearSelections();
62049         }
62050         for(var i = 0, len = rows.length; i < len; i++){
62051             this.selectRow(rows[i], true);
62052         }
62053     },
62054
62055     /**
62056      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62057      * @param {Number} startRow The index of the first row in the range
62058      * @param {Number} endRow The index of the last row in the range
62059      * @param {Boolean} keepExisting (optional) True to retain existing selections
62060      */
62061     selectRange : function(startRow, endRow, keepExisting){
62062         if(this.locked) {
62063             return;
62064         }
62065         if(!keepExisting){
62066             this.clearSelections();
62067         }
62068         if(startRow <= endRow){
62069             for(var i = startRow; i <= endRow; i++){
62070                 this.selectRow(i, true);
62071             }
62072         }else{
62073             for(var i = startRow; i >= endRow; i--){
62074                 this.selectRow(i, true);
62075             }
62076         }
62077     },
62078
62079     /**
62080      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62081      * @param {Number} startRow The index of the first row in the range
62082      * @param {Number} endRow The index of the last row in the range
62083      */
62084     deselectRange : function(startRow, endRow, preventViewNotify){
62085         if(this.locked) {
62086             return;
62087         }
62088         for(var i = startRow; i <= endRow; i++){
62089             this.deselectRow(i, preventViewNotify);
62090         }
62091     },
62092
62093     /**
62094      * Selects a row.
62095      * @param {Number} row The index of the row to select
62096      * @param {Boolean} keepExisting (optional) True to keep existing selections
62097      */
62098     selectRow : function(index, keepExisting, preventViewNotify){
62099         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62100             return;
62101         }
62102         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62103             if(!keepExisting || this.singleSelect){
62104                 this.clearSelections();
62105             }
62106             var r = this.grid.ds.getAt(index);
62107             this.selections.add(r);
62108             this.last = this.lastActive = index;
62109             if(!preventViewNotify){
62110                 var view = this.grid.view ? this.grid.view : this.grid;
62111                 view.onRowSelect(index);
62112             }
62113             this.fireEvent("rowselect", this, index, r);
62114             this.fireEvent("selectionchange", this);
62115         }
62116     },
62117
62118     /**
62119      * Deselects a row.
62120      * @param {Number} row The index of the row to deselect
62121      */
62122     deselectRow : function(index, preventViewNotify){
62123         if(this.locked) {
62124             return;
62125         }
62126         if(this.last == index){
62127             this.last = false;
62128         }
62129         if(this.lastActive == index){
62130             this.lastActive = false;
62131         }
62132         var r = this.grid.ds.getAt(index);
62133         this.selections.remove(r);
62134         if(!preventViewNotify){
62135             var view = this.grid.view ? this.grid.view : this.grid;
62136             view.onRowDeselect(index);
62137         }
62138         this.fireEvent("rowdeselect", this, index);
62139         this.fireEvent("selectionchange", this);
62140     },
62141
62142     // private
62143     restoreLast : function(){
62144         if(this._last){
62145             this.last = this._last;
62146         }
62147     },
62148
62149     // private
62150     acceptsNav : function(row, col, cm){
62151         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62152     },
62153
62154     // private
62155     onEditorKey : function(field, e){
62156         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62157         if(k == e.TAB){
62158             e.stopEvent();
62159             ed.completeEdit();
62160             if(e.shiftKey){
62161                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62162             }else{
62163                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62164             }
62165         }else if(k == e.ENTER && !e.ctrlKey){
62166             e.stopEvent();
62167             ed.completeEdit();
62168             if(e.shiftKey){
62169                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62170             }else{
62171                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62172             }
62173         }else if(k == e.ESC){
62174             ed.cancelEdit();
62175         }
62176         if(newCell){
62177             g.startEditing(newCell[0], newCell[1]);
62178         }
62179     }
62180 });/*
62181  * Based on:
62182  * Ext JS Library 1.1.1
62183  * Copyright(c) 2006-2007, Ext JS, LLC.
62184  *
62185  * Originally Released Under LGPL - original licence link has changed is not relivant.
62186  *
62187  * Fork - LGPL
62188  * <script type="text/javascript">
62189  */
62190 /**
62191  * @class Roo.grid.CellSelectionModel
62192  * @extends Roo.grid.AbstractSelectionModel
62193  * This class provides the basic implementation for cell selection in a grid.
62194  * @constructor
62195  * @param {Object} config The object containing the configuration of this model.
62196  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62197  */
62198 Roo.grid.CellSelectionModel = function(config){
62199     Roo.apply(this, config);
62200
62201     this.selection = null;
62202
62203     this.addEvents({
62204         /**
62205              * @event beforerowselect
62206              * Fires before a cell is selected.
62207              * @param {SelectionModel} this
62208              * @param {Number} rowIndex The selected row index
62209              * @param {Number} colIndex The selected cell index
62210              */
62211             "beforecellselect" : true,
62212         /**
62213              * @event cellselect
62214              * Fires when a cell is selected.
62215              * @param {SelectionModel} this
62216              * @param {Number} rowIndex The selected row index
62217              * @param {Number} colIndex The selected cell index
62218              */
62219             "cellselect" : true,
62220         /**
62221              * @event selectionchange
62222              * Fires when the active selection changes.
62223              * @param {SelectionModel} this
62224              * @param {Object} selection null for no selection or an object (o) with two properties
62225                 <ul>
62226                 <li>o.record: the record object for the row the selection is in</li>
62227                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62228                 </ul>
62229              */
62230             "selectionchange" : true,
62231         /**
62232              * @event tabend
62233              * Fires when the tab (or enter) was pressed on the last editable cell
62234              * You can use this to trigger add new row.
62235              * @param {SelectionModel} this
62236              */
62237             "tabend" : true,
62238          /**
62239              * @event beforeeditnext
62240              * Fires before the next editable sell is made active
62241              * You can use this to skip to another cell or fire the tabend
62242              *    if you set cell to false
62243              * @param {Object} eventdata object : { cell : [ row, col ] } 
62244              */
62245             "beforeeditnext" : true
62246     });
62247     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62248 };
62249
62250 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62251     
62252     enter_is_tab: false,
62253
62254     /** @ignore */
62255     initEvents : function(){
62256         this.grid.on("mousedown", this.handleMouseDown, this);
62257         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62258         var view = this.grid.view;
62259         view.on("refresh", this.onViewChange, this);
62260         view.on("rowupdated", this.onRowUpdated, this);
62261         view.on("beforerowremoved", this.clearSelections, this);
62262         view.on("beforerowsinserted", this.clearSelections, this);
62263         if(this.grid.isEditor){
62264             this.grid.on("beforeedit", this.beforeEdit,  this);
62265         }
62266     },
62267
62268         //private
62269     beforeEdit : function(e){
62270         this.select(e.row, e.column, false, true, e.record);
62271     },
62272
62273         //private
62274     onRowUpdated : function(v, index, r){
62275         if(this.selection && this.selection.record == r){
62276             v.onCellSelect(index, this.selection.cell[1]);
62277         }
62278     },
62279
62280         //private
62281     onViewChange : function(){
62282         this.clearSelections(true);
62283     },
62284
62285         /**
62286          * Returns the currently selected cell,.
62287          * @return {Array} The selected cell (row, column) or null if none selected.
62288          */
62289     getSelectedCell : function(){
62290         return this.selection ? this.selection.cell : null;
62291     },
62292
62293     /**
62294      * Clears all selections.
62295      * @param {Boolean} true to prevent the gridview from being notified about the change.
62296      */
62297     clearSelections : function(preventNotify){
62298         var s = this.selection;
62299         if(s){
62300             if(preventNotify !== true){
62301                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62302             }
62303             this.selection = null;
62304             this.fireEvent("selectionchange", this, null);
62305         }
62306     },
62307
62308     /**
62309      * Returns true if there is a selection.
62310      * @return {Boolean}
62311      */
62312     hasSelection : function(){
62313         return this.selection ? true : false;
62314     },
62315
62316     /** @ignore */
62317     handleMouseDown : function(e, t){
62318         var v = this.grid.getView();
62319         if(this.isLocked()){
62320             return;
62321         };
62322         var row = v.findRowIndex(t);
62323         var cell = v.findCellIndex(t);
62324         if(row !== false && cell !== false){
62325             this.select(row, cell);
62326         }
62327     },
62328
62329     /**
62330      * Selects a cell.
62331      * @param {Number} rowIndex
62332      * @param {Number} collIndex
62333      */
62334     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62335         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62336             this.clearSelections();
62337             r = r || this.grid.dataSource.getAt(rowIndex);
62338             this.selection = {
62339                 record : r,
62340                 cell : [rowIndex, colIndex]
62341             };
62342             if(!preventViewNotify){
62343                 var v = this.grid.getView();
62344                 v.onCellSelect(rowIndex, colIndex);
62345                 if(preventFocus !== true){
62346                     v.focusCell(rowIndex, colIndex);
62347                 }
62348             }
62349             this.fireEvent("cellselect", this, rowIndex, colIndex);
62350             this.fireEvent("selectionchange", this, this.selection);
62351         }
62352     },
62353
62354         //private
62355     isSelectable : function(rowIndex, colIndex, cm){
62356         return !cm.isHidden(colIndex);
62357     },
62358
62359     /** @ignore */
62360     handleKeyDown : function(e){
62361         //Roo.log('Cell Sel Model handleKeyDown');
62362         if(!e.isNavKeyPress()){
62363             return;
62364         }
62365         var g = this.grid, s = this.selection;
62366         if(!s){
62367             e.stopEvent();
62368             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62369             if(cell){
62370                 this.select(cell[0], cell[1]);
62371             }
62372             return;
62373         }
62374         var sm = this;
62375         var walk = function(row, col, step){
62376             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62377         };
62378         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62379         var newCell;
62380
62381       
62382
62383         switch(k){
62384             case e.TAB:
62385                 // handled by onEditorKey
62386                 if (g.isEditor && g.editing) {
62387                     return;
62388                 }
62389                 if(e.shiftKey) {
62390                     newCell = walk(r, c-1, -1);
62391                 } else {
62392                     newCell = walk(r, c+1, 1);
62393                 }
62394                 break;
62395             
62396             case e.DOWN:
62397                newCell = walk(r+1, c, 1);
62398                 break;
62399             
62400             case e.UP:
62401                 newCell = walk(r-1, c, -1);
62402                 break;
62403             
62404             case e.RIGHT:
62405                 newCell = walk(r, c+1, 1);
62406                 break;
62407             
62408             case e.LEFT:
62409                 newCell = walk(r, c-1, -1);
62410                 break;
62411             
62412             case e.ENTER:
62413                 
62414                 if(g.isEditor && !g.editing){
62415                    g.startEditing(r, c);
62416                    e.stopEvent();
62417                    return;
62418                 }
62419                 
62420                 
62421              break;
62422         };
62423         if(newCell){
62424             this.select(newCell[0], newCell[1]);
62425             e.stopEvent();
62426             
62427         }
62428     },
62429
62430     acceptsNav : function(row, col, cm){
62431         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62432     },
62433     /**
62434      * Selects a cell.
62435      * @param {Number} field (not used) - as it's normally used as a listener
62436      * @param {Number} e - event - fake it by using
62437      *
62438      * var e = Roo.EventObjectImpl.prototype;
62439      * e.keyCode = e.TAB
62440      *
62441      * 
62442      */
62443     onEditorKey : function(field, e){
62444         
62445         var k = e.getKey(),
62446             newCell,
62447             g = this.grid,
62448             ed = g.activeEditor,
62449             forward = false;
62450         ///Roo.log('onEditorKey' + k);
62451         
62452         
62453         if (this.enter_is_tab && k == e.ENTER) {
62454             k = e.TAB;
62455         }
62456         
62457         if(k == e.TAB){
62458             if(e.shiftKey){
62459                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62460             }else{
62461                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62462                 forward = true;
62463             }
62464             
62465             e.stopEvent();
62466             
62467         } else if(k == e.ENTER &&  !e.ctrlKey){
62468             ed.completeEdit();
62469             e.stopEvent();
62470             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62471         
62472                 } else if(k == e.ESC){
62473             ed.cancelEdit();
62474         }
62475                 
62476         if (newCell) {
62477             var ecall = { cell : newCell, forward : forward };
62478             this.fireEvent('beforeeditnext', ecall );
62479             newCell = ecall.cell;
62480                         forward = ecall.forward;
62481         }
62482                 
62483         if(newCell){
62484             //Roo.log('next cell after edit');
62485             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62486         } else if (forward) {
62487             // tabbed past last
62488             this.fireEvent.defer(100, this, ['tabend',this]);
62489         }
62490     }
62491 });/*
62492  * Based on:
62493  * Ext JS Library 1.1.1
62494  * Copyright(c) 2006-2007, Ext JS, LLC.
62495  *
62496  * Originally Released Under LGPL - original licence link has changed is not relivant.
62497  *
62498  * Fork - LGPL
62499  * <script type="text/javascript">
62500  */
62501  
62502 /**
62503  * @class Roo.grid.EditorGrid
62504  * @extends Roo.grid.Grid
62505  * Class for creating and editable grid.
62506  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62507  * The container MUST have some type of size defined for the grid to fill. The container will be 
62508  * automatically set to position relative if it isn't already.
62509  * @param {Object} dataSource The data model to bind to
62510  * @param {Object} colModel The column model with info about this grid's columns
62511  */
62512 Roo.grid.EditorGrid = function(container, config){
62513     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62514     this.getGridEl().addClass("xedit-grid");
62515
62516     if(!this.selModel){
62517         this.selModel = new Roo.grid.CellSelectionModel();
62518     }
62519
62520     this.activeEditor = null;
62521
62522         this.addEvents({
62523             /**
62524              * @event beforeedit
62525              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62526              * <ul style="padding:5px;padding-left:16px;">
62527              * <li>grid - This grid</li>
62528              * <li>record - The record being edited</li>
62529              * <li>field - The field name being edited</li>
62530              * <li>value - The value for the field being edited.</li>
62531              * <li>row - The grid row index</li>
62532              * <li>column - The grid column index</li>
62533              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62534              * </ul>
62535              * @param {Object} e An edit event (see above for description)
62536              */
62537             "beforeedit" : true,
62538             /**
62539              * @event afteredit
62540              * Fires after a cell is edited. <br />
62541              * <ul style="padding:5px;padding-left:16px;">
62542              * <li>grid - This grid</li>
62543              * <li>record - The record being edited</li>
62544              * <li>field - The field name being edited</li>
62545              * <li>value - The value being set</li>
62546              * <li>originalValue - The original value for the field, before the edit.</li>
62547              * <li>row - The grid row index</li>
62548              * <li>column - The grid column index</li>
62549              * </ul>
62550              * @param {Object} e An edit event (see above for description)
62551              */
62552             "afteredit" : true,
62553             /**
62554              * @event validateedit
62555              * Fires after a cell is edited, but before the value is set in the record. 
62556          * You can use this to modify the value being set in the field, Return false
62557              * to cancel the change. The edit event object has the following properties <br />
62558              * <ul style="padding:5px;padding-left:16px;">
62559          * <li>editor - This editor</li>
62560              * <li>grid - This grid</li>
62561              * <li>record - The record being edited</li>
62562              * <li>field - The field name being edited</li>
62563              * <li>value - The value being set</li>
62564              * <li>originalValue - The original value for the field, before the edit.</li>
62565              * <li>row - The grid row index</li>
62566              * <li>column - The grid column index</li>
62567              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62568              * </ul>
62569              * @param {Object} e An edit event (see above for description)
62570              */
62571             "validateedit" : true
62572         });
62573     this.on("bodyscroll", this.stopEditing,  this);
62574     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62575 };
62576
62577 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62578     /**
62579      * @cfg {Number} clicksToEdit
62580      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62581      */
62582     clicksToEdit: 2,
62583
62584     // private
62585     isEditor : true,
62586     // private
62587     trackMouseOver: false, // causes very odd FF errors
62588
62589     onCellDblClick : function(g, row, col){
62590         this.startEditing(row, col);
62591     },
62592
62593     onEditComplete : function(ed, value, startValue){
62594         this.editing = false;
62595         this.activeEditor = null;
62596         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62597         var r = ed.record;
62598         var field = this.colModel.getDataIndex(ed.col);
62599         var e = {
62600             grid: this,
62601             record: r,
62602             field: field,
62603             originalValue: startValue,
62604             value: value,
62605             row: ed.row,
62606             column: ed.col,
62607             cancel:false,
62608             editor: ed
62609         };
62610         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62611         cell.show();
62612           
62613         if(String(value) !== String(startValue)){
62614             
62615             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62616                 r.set(field, e.value);
62617                 // if we are dealing with a combo box..
62618                 // then we also set the 'name' colum to be the displayField
62619                 if (ed.field.displayField && ed.field.name) {
62620                     r.set(ed.field.name, ed.field.el.dom.value);
62621                 }
62622                 
62623                 delete e.cancel; //?? why!!!
62624                 this.fireEvent("afteredit", e);
62625             }
62626         } else {
62627             this.fireEvent("afteredit", e); // always fire it!
62628         }
62629         this.view.focusCell(ed.row, ed.col);
62630     },
62631
62632     /**
62633      * Starts editing the specified for the specified row/column
62634      * @param {Number} rowIndex
62635      * @param {Number} colIndex
62636      */
62637     startEditing : function(row, col){
62638         this.stopEditing();
62639         if(this.colModel.isCellEditable(col, row)){
62640             this.view.ensureVisible(row, col, true);
62641           
62642             var r = this.dataSource.getAt(row);
62643             var field = this.colModel.getDataIndex(col);
62644             var cell = Roo.get(this.view.getCell(row,col));
62645             var e = {
62646                 grid: this,
62647                 record: r,
62648                 field: field,
62649                 value: r.data[field],
62650                 row: row,
62651                 column: col,
62652                 cancel:false 
62653             };
62654             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62655                 this.editing = true;
62656                 var ed = this.colModel.getCellEditor(col, row);
62657                 
62658                 if (!ed) {
62659                     return;
62660                 }
62661                 if(!ed.rendered){
62662                     ed.render(ed.parentEl || document.body);
62663                 }
62664                 ed.field.reset();
62665                
62666                 cell.hide();
62667                 
62668                 (function(){ // complex but required for focus issues in safari, ie and opera
62669                     ed.row = row;
62670                     ed.col = col;
62671                     ed.record = r;
62672                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62673                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62674                     this.activeEditor = ed;
62675                     var v = r.data[field];
62676                     ed.startEdit(this.view.getCell(row, col), v);
62677                     // combo's with 'displayField and name set
62678                     if (ed.field.displayField && ed.field.name) {
62679                         ed.field.el.dom.value = r.data[ed.field.name];
62680                     }
62681                     
62682                     
62683                 }).defer(50, this);
62684             }
62685         }
62686     },
62687         
62688     /**
62689      * Stops any active editing
62690      */
62691     stopEditing : function(){
62692         if(this.activeEditor){
62693             this.activeEditor.completeEdit();
62694         }
62695         this.activeEditor = null;
62696     },
62697         
62698          /**
62699      * Called to get grid's drag proxy text, by default returns this.ddText.
62700      * @return {String}
62701      */
62702     getDragDropText : function(){
62703         var count = this.selModel.getSelectedCell() ? 1 : 0;
62704         return String.format(this.ddText, count, count == 1 ? '' : 's');
62705     }
62706         
62707 });/*
62708  * Based on:
62709  * Ext JS Library 1.1.1
62710  * Copyright(c) 2006-2007, Ext JS, LLC.
62711  *
62712  * Originally Released Under LGPL - original licence link has changed is not relivant.
62713  *
62714  * Fork - LGPL
62715  * <script type="text/javascript">
62716  */
62717
62718 // private - not really -- you end up using it !
62719 // This is a support class used internally by the Grid components
62720
62721 /**
62722  * @class Roo.grid.GridEditor
62723  * @extends Roo.Editor
62724  * Class for creating and editable grid elements.
62725  * @param {Object} config any settings (must include field)
62726  */
62727 Roo.grid.GridEditor = function(field, config){
62728     if (!config && field.field) {
62729         config = field;
62730         field = Roo.factory(config.field, Roo.form);
62731     }
62732     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62733     field.monitorTab = false;
62734 };
62735
62736 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62737     
62738     /**
62739      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62740      */
62741     
62742     alignment: "tl-tl",
62743     autoSize: "width",
62744     hideEl : false,
62745     cls: "x-small-editor x-grid-editor",
62746     shim:false,
62747     shadow:"frame"
62748 });/*
62749  * Based on:
62750  * Ext JS Library 1.1.1
62751  * Copyright(c) 2006-2007, Ext JS, LLC.
62752  *
62753  * Originally Released Under LGPL - original licence link has changed is not relivant.
62754  *
62755  * Fork - LGPL
62756  * <script type="text/javascript">
62757  */
62758   
62759
62760   
62761 Roo.grid.PropertyRecord = Roo.data.Record.create([
62762     {name:'name',type:'string'},  'value'
62763 ]);
62764
62765
62766 Roo.grid.PropertyStore = function(grid, source){
62767     this.grid = grid;
62768     this.store = new Roo.data.Store({
62769         recordType : Roo.grid.PropertyRecord
62770     });
62771     this.store.on('update', this.onUpdate,  this);
62772     if(source){
62773         this.setSource(source);
62774     }
62775     Roo.grid.PropertyStore.superclass.constructor.call(this);
62776 };
62777
62778
62779
62780 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62781     setSource : function(o){
62782         this.source = o;
62783         this.store.removeAll();
62784         var data = [];
62785         for(var k in o){
62786             if(this.isEditableValue(o[k])){
62787                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62788             }
62789         }
62790         this.store.loadRecords({records: data}, {}, true);
62791     },
62792
62793     onUpdate : function(ds, record, type){
62794         if(type == Roo.data.Record.EDIT){
62795             var v = record.data['value'];
62796             var oldValue = record.modified['value'];
62797             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62798                 this.source[record.id] = v;
62799                 record.commit();
62800                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62801             }else{
62802                 record.reject();
62803             }
62804         }
62805     },
62806
62807     getProperty : function(row){
62808        return this.store.getAt(row);
62809     },
62810
62811     isEditableValue: function(val){
62812         if(val && val instanceof Date){
62813             return true;
62814         }else if(typeof val == 'object' || typeof val == 'function'){
62815             return false;
62816         }
62817         return true;
62818     },
62819
62820     setValue : function(prop, value){
62821         this.source[prop] = value;
62822         this.store.getById(prop).set('value', value);
62823     },
62824
62825     getSource : function(){
62826         return this.source;
62827     }
62828 });
62829
62830 Roo.grid.PropertyColumnModel = function(grid, store){
62831     this.grid = grid;
62832     var g = Roo.grid;
62833     g.PropertyColumnModel.superclass.constructor.call(this, [
62834         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62835         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62836     ]);
62837     this.store = store;
62838     this.bselect = Roo.DomHelper.append(document.body, {
62839         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62840             {tag: 'option', value: 'true', html: 'true'},
62841             {tag: 'option', value: 'false', html: 'false'}
62842         ]
62843     });
62844     Roo.id(this.bselect);
62845     var f = Roo.form;
62846     this.editors = {
62847         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62848         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62849         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62850         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62851         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62852     };
62853     this.renderCellDelegate = this.renderCell.createDelegate(this);
62854     this.renderPropDelegate = this.renderProp.createDelegate(this);
62855 };
62856
62857 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62858     
62859     
62860     nameText : 'Name',
62861     valueText : 'Value',
62862     
62863     dateFormat : 'm/j/Y',
62864     
62865     
62866     renderDate : function(dateVal){
62867         return dateVal.dateFormat(this.dateFormat);
62868     },
62869
62870     renderBool : function(bVal){
62871         return bVal ? 'true' : 'false';
62872     },
62873
62874     isCellEditable : function(colIndex, rowIndex){
62875         return colIndex == 1;
62876     },
62877
62878     getRenderer : function(col){
62879         return col == 1 ?
62880             this.renderCellDelegate : this.renderPropDelegate;
62881     },
62882
62883     renderProp : function(v){
62884         return this.getPropertyName(v);
62885     },
62886
62887     renderCell : function(val){
62888         var rv = val;
62889         if(val instanceof Date){
62890             rv = this.renderDate(val);
62891         }else if(typeof val == 'boolean'){
62892             rv = this.renderBool(val);
62893         }
62894         return Roo.util.Format.htmlEncode(rv);
62895     },
62896
62897     getPropertyName : function(name){
62898         var pn = this.grid.propertyNames;
62899         return pn && pn[name] ? pn[name] : name;
62900     },
62901
62902     getCellEditor : function(colIndex, rowIndex){
62903         var p = this.store.getProperty(rowIndex);
62904         var n = p.data['name'], val = p.data['value'];
62905         
62906         if(typeof(this.grid.customEditors[n]) == 'string'){
62907             return this.editors[this.grid.customEditors[n]];
62908         }
62909         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62910             return this.grid.customEditors[n];
62911         }
62912         if(val instanceof Date){
62913             return this.editors['date'];
62914         }else if(typeof val == 'number'){
62915             return this.editors['number'];
62916         }else if(typeof val == 'boolean'){
62917             return this.editors['boolean'];
62918         }else{
62919             return this.editors['string'];
62920         }
62921     }
62922 });
62923
62924 /**
62925  * @class Roo.grid.PropertyGrid
62926  * @extends Roo.grid.EditorGrid
62927  * This class represents the  interface of a component based property grid control.
62928  * <br><br>Usage:<pre><code>
62929  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62930       
62931  });
62932  // set any options
62933  grid.render();
62934  * </code></pre>
62935   
62936  * @constructor
62937  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62938  * The container MUST have some type of size defined for the grid to fill. The container will be
62939  * automatically set to position relative if it isn't already.
62940  * @param {Object} config A config object that sets properties on this grid.
62941  */
62942 Roo.grid.PropertyGrid = function(container, config){
62943     config = config || {};
62944     var store = new Roo.grid.PropertyStore(this);
62945     this.store = store;
62946     var cm = new Roo.grid.PropertyColumnModel(this, store);
62947     store.store.sort('name', 'ASC');
62948     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62949         ds: store.store,
62950         cm: cm,
62951         enableColLock:false,
62952         enableColumnMove:false,
62953         stripeRows:false,
62954         trackMouseOver: false,
62955         clicksToEdit:1
62956     }, config));
62957     this.getGridEl().addClass('x-props-grid');
62958     this.lastEditRow = null;
62959     this.on('columnresize', this.onColumnResize, this);
62960     this.addEvents({
62961          /**
62962              * @event beforepropertychange
62963              * Fires before a property changes (return false to stop?)
62964              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62965              * @param {String} id Record Id
62966              * @param {String} newval New Value
62967          * @param {String} oldval Old Value
62968              */
62969         "beforepropertychange": true,
62970         /**
62971              * @event propertychange
62972              * Fires after a property changes
62973              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62974              * @param {String} id Record Id
62975              * @param {String} newval New Value
62976          * @param {String} oldval Old Value
62977              */
62978         "propertychange": true
62979     });
62980     this.customEditors = this.customEditors || {};
62981 };
62982 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62983     
62984      /**
62985      * @cfg {Object} customEditors map of colnames=> custom editors.
62986      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62987      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62988      * false disables editing of the field.
62989          */
62990     
62991       /**
62992      * @cfg {Object} propertyNames map of property Names to their displayed value
62993          */
62994     
62995     render : function(){
62996         Roo.grid.PropertyGrid.superclass.render.call(this);
62997         this.autoSize.defer(100, this);
62998     },
62999
63000     autoSize : function(){
63001         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
63002         if(this.view){
63003             this.view.fitColumns();
63004         }
63005     },
63006
63007     onColumnResize : function(){
63008         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
63009         this.autoSize();
63010     },
63011     /**
63012      * Sets the data for the Grid
63013      * accepts a Key => Value object of all the elements avaiable.
63014      * @param {Object} data  to appear in grid.
63015      */
63016     setSource : function(source){
63017         this.store.setSource(source);
63018         //this.autoSize();
63019     },
63020     /**
63021      * Gets all the data from the grid.
63022      * @return {Object} data  data stored in grid
63023      */
63024     getSource : function(){
63025         return this.store.getSource();
63026     }
63027 });/*
63028   
63029  * Licence LGPL
63030  
63031  */
63032  
63033 /**
63034  * @class Roo.grid.Calendar
63035  * @extends Roo.grid.Grid
63036  * This class extends the Grid to provide a calendar widget
63037  * <br><br>Usage:<pre><code>
63038  var grid = new Roo.grid.Calendar("my-container-id", {
63039      ds: myDataStore,
63040      cm: myColModel,
63041      selModel: mySelectionModel,
63042      autoSizeColumns: true,
63043      monitorWindowResize: false,
63044      trackMouseOver: true
63045      eventstore : real data store..
63046  });
63047  // set any options
63048  grid.render();
63049   
63050   * @constructor
63051  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63052  * The container MUST have some type of size defined for the grid to fill. The container will be
63053  * automatically set to position relative if it isn't already.
63054  * @param {Object} config A config object that sets properties on this grid.
63055  */
63056 Roo.grid.Calendar = function(container, config){
63057         // initialize the container
63058         this.container = Roo.get(container);
63059         this.container.update("");
63060         this.container.setStyle("overflow", "hidden");
63061     this.container.addClass('x-grid-container');
63062
63063     this.id = this.container.id;
63064
63065     Roo.apply(this, config);
63066     // check and correct shorthanded configs
63067     
63068     var rows = [];
63069     var d =1;
63070     for (var r = 0;r < 6;r++) {
63071         
63072         rows[r]=[];
63073         for (var c =0;c < 7;c++) {
63074             rows[r][c]= '';
63075         }
63076     }
63077     if (this.eventStore) {
63078         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63079         this.eventStore.on('load',this.onLoad, this);
63080         this.eventStore.on('beforeload',this.clearEvents, this);
63081          
63082     }
63083     
63084     this.dataSource = new Roo.data.Store({
63085             proxy: new Roo.data.MemoryProxy(rows),
63086             reader: new Roo.data.ArrayReader({}, [
63087                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63088     });
63089
63090     this.dataSource.load();
63091     this.ds = this.dataSource;
63092     this.ds.xmodule = this.xmodule || false;
63093     
63094     
63095     var cellRender = function(v,x,r)
63096     {
63097         return String.format(
63098             '<div class="fc-day  fc-widget-content"><div>' +
63099                 '<div class="fc-event-container"></div>' +
63100                 '<div class="fc-day-number">{0}</div>'+
63101                 
63102                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63103             '</div></div>', v);
63104     
63105     }
63106     
63107     
63108     this.colModel = new Roo.grid.ColumnModel( [
63109         {
63110             xtype: 'ColumnModel',
63111             xns: Roo.grid,
63112             dataIndex : 'weekday0',
63113             header : 'Sunday',
63114             renderer : cellRender
63115         },
63116         {
63117             xtype: 'ColumnModel',
63118             xns: Roo.grid,
63119             dataIndex : 'weekday1',
63120             header : 'Monday',
63121             renderer : cellRender
63122         },
63123         {
63124             xtype: 'ColumnModel',
63125             xns: Roo.grid,
63126             dataIndex : 'weekday2',
63127             header : 'Tuesday',
63128             renderer : cellRender
63129         },
63130         {
63131             xtype: 'ColumnModel',
63132             xns: Roo.grid,
63133             dataIndex : 'weekday3',
63134             header : 'Wednesday',
63135             renderer : cellRender
63136         },
63137         {
63138             xtype: 'ColumnModel',
63139             xns: Roo.grid,
63140             dataIndex : 'weekday4',
63141             header : 'Thursday',
63142             renderer : cellRender
63143         },
63144         {
63145             xtype: 'ColumnModel',
63146             xns: Roo.grid,
63147             dataIndex : 'weekday5',
63148             header : 'Friday',
63149             renderer : cellRender
63150         },
63151         {
63152             xtype: 'ColumnModel',
63153             xns: Roo.grid,
63154             dataIndex : 'weekday6',
63155             header : 'Saturday',
63156             renderer : cellRender
63157         }
63158     ]);
63159     this.cm = this.colModel;
63160     this.cm.xmodule = this.xmodule || false;
63161  
63162         
63163           
63164     //this.selModel = new Roo.grid.CellSelectionModel();
63165     //this.sm = this.selModel;
63166     //this.selModel.init(this);
63167     
63168     
63169     if(this.width){
63170         this.container.setWidth(this.width);
63171     }
63172
63173     if(this.height){
63174         this.container.setHeight(this.height);
63175     }
63176     /** @private */
63177         this.addEvents({
63178         // raw events
63179         /**
63180          * @event click
63181          * The raw click event for the entire grid.
63182          * @param {Roo.EventObject} e
63183          */
63184         "click" : true,
63185         /**
63186          * @event dblclick
63187          * The raw dblclick event for the entire grid.
63188          * @param {Roo.EventObject} e
63189          */
63190         "dblclick" : true,
63191         /**
63192          * @event contextmenu
63193          * The raw contextmenu event for the entire grid.
63194          * @param {Roo.EventObject} e
63195          */
63196         "contextmenu" : true,
63197         /**
63198          * @event mousedown
63199          * The raw mousedown event for the entire grid.
63200          * @param {Roo.EventObject} e
63201          */
63202         "mousedown" : true,
63203         /**
63204          * @event mouseup
63205          * The raw mouseup event for the entire grid.
63206          * @param {Roo.EventObject} e
63207          */
63208         "mouseup" : true,
63209         /**
63210          * @event mouseover
63211          * The raw mouseover event for the entire grid.
63212          * @param {Roo.EventObject} e
63213          */
63214         "mouseover" : true,
63215         /**
63216          * @event mouseout
63217          * The raw mouseout event for the entire grid.
63218          * @param {Roo.EventObject} e
63219          */
63220         "mouseout" : true,
63221         /**
63222          * @event keypress
63223          * The raw keypress event for the entire grid.
63224          * @param {Roo.EventObject} e
63225          */
63226         "keypress" : true,
63227         /**
63228          * @event keydown
63229          * The raw keydown event for the entire grid.
63230          * @param {Roo.EventObject} e
63231          */
63232         "keydown" : true,
63233
63234         // custom events
63235
63236         /**
63237          * @event cellclick
63238          * Fires when a cell is clicked
63239          * @param {Grid} this
63240          * @param {Number} rowIndex
63241          * @param {Number} columnIndex
63242          * @param {Roo.EventObject} e
63243          */
63244         "cellclick" : true,
63245         /**
63246          * @event celldblclick
63247          * Fires when a cell is double clicked
63248          * @param {Grid} this
63249          * @param {Number} rowIndex
63250          * @param {Number} columnIndex
63251          * @param {Roo.EventObject} e
63252          */
63253         "celldblclick" : true,
63254         /**
63255          * @event rowclick
63256          * Fires when a row is clicked
63257          * @param {Grid} this
63258          * @param {Number} rowIndex
63259          * @param {Roo.EventObject} e
63260          */
63261         "rowclick" : true,
63262         /**
63263          * @event rowdblclick
63264          * Fires when a row is double clicked
63265          * @param {Grid} this
63266          * @param {Number} rowIndex
63267          * @param {Roo.EventObject} e
63268          */
63269         "rowdblclick" : true,
63270         /**
63271          * @event headerclick
63272          * Fires when a header is clicked
63273          * @param {Grid} this
63274          * @param {Number} columnIndex
63275          * @param {Roo.EventObject} e
63276          */
63277         "headerclick" : true,
63278         /**
63279          * @event headerdblclick
63280          * Fires when a header cell is double clicked
63281          * @param {Grid} this
63282          * @param {Number} columnIndex
63283          * @param {Roo.EventObject} e
63284          */
63285         "headerdblclick" : true,
63286         /**
63287          * @event rowcontextmenu
63288          * Fires when a row is right clicked
63289          * @param {Grid} this
63290          * @param {Number} rowIndex
63291          * @param {Roo.EventObject} e
63292          */
63293         "rowcontextmenu" : true,
63294         /**
63295          * @event cellcontextmenu
63296          * Fires when a cell is right clicked
63297          * @param {Grid} this
63298          * @param {Number} rowIndex
63299          * @param {Number} cellIndex
63300          * @param {Roo.EventObject} e
63301          */
63302          "cellcontextmenu" : true,
63303         /**
63304          * @event headercontextmenu
63305          * Fires when a header is right clicked
63306          * @param {Grid} this
63307          * @param {Number} columnIndex
63308          * @param {Roo.EventObject} e
63309          */
63310         "headercontextmenu" : true,
63311         /**
63312          * @event bodyscroll
63313          * Fires when the body element is scrolled
63314          * @param {Number} scrollLeft
63315          * @param {Number} scrollTop
63316          */
63317         "bodyscroll" : true,
63318         /**
63319          * @event columnresize
63320          * Fires when the user resizes a column
63321          * @param {Number} columnIndex
63322          * @param {Number} newSize
63323          */
63324         "columnresize" : true,
63325         /**
63326          * @event columnmove
63327          * Fires when the user moves a column
63328          * @param {Number} oldIndex
63329          * @param {Number} newIndex
63330          */
63331         "columnmove" : true,
63332         /**
63333          * @event startdrag
63334          * Fires when row(s) start being dragged
63335          * @param {Grid} this
63336          * @param {Roo.GridDD} dd The drag drop object
63337          * @param {event} e The raw browser event
63338          */
63339         "startdrag" : true,
63340         /**
63341          * @event enddrag
63342          * Fires when a drag operation is complete
63343          * @param {Grid} this
63344          * @param {Roo.GridDD} dd The drag drop object
63345          * @param {event} e The raw browser event
63346          */
63347         "enddrag" : true,
63348         /**
63349          * @event dragdrop
63350          * Fires when dragged row(s) are dropped on a valid DD target
63351          * @param {Grid} this
63352          * @param {Roo.GridDD} dd The drag drop object
63353          * @param {String} targetId The target drag drop object
63354          * @param {event} e The raw browser event
63355          */
63356         "dragdrop" : true,
63357         /**
63358          * @event dragover
63359          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63360          * @param {Grid} this
63361          * @param {Roo.GridDD} dd The drag drop object
63362          * @param {String} targetId The target drag drop object
63363          * @param {event} e The raw browser event
63364          */
63365         "dragover" : true,
63366         /**
63367          * @event dragenter
63368          *  Fires when the dragged row(s) first cross another DD target while being dragged
63369          * @param {Grid} this
63370          * @param {Roo.GridDD} dd The drag drop object
63371          * @param {String} targetId The target drag drop object
63372          * @param {event} e The raw browser event
63373          */
63374         "dragenter" : true,
63375         /**
63376          * @event dragout
63377          * Fires when the dragged row(s) leave another DD target while being dragged
63378          * @param {Grid} this
63379          * @param {Roo.GridDD} dd The drag drop object
63380          * @param {String} targetId The target drag drop object
63381          * @param {event} e The raw browser event
63382          */
63383         "dragout" : true,
63384         /**
63385          * @event rowclass
63386          * Fires when a row is rendered, so you can change add a style to it.
63387          * @param {GridView} gridview   The grid view
63388          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63389          */
63390         'rowclass' : true,
63391
63392         /**
63393          * @event render
63394          * Fires when the grid is rendered
63395          * @param {Grid} grid
63396          */
63397         'render' : true,
63398             /**
63399              * @event select
63400              * Fires when a date is selected
63401              * @param {DatePicker} this
63402              * @param {Date} date The selected date
63403              */
63404         'select': true,
63405         /**
63406              * @event monthchange
63407              * Fires when the displayed month changes 
63408              * @param {DatePicker} this
63409              * @param {Date} date The selected month
63410              */
63411         'monthchange': true,
63412         /**
63413              * @event evententer
63414              * Fires when mouse over an event
63415              * @param {Calendar} this
63416              * @param {event} Event
63417              */
63418         'evententer': true,
63419         /**
63420              * @event eventleave
63421              * Fires when the mouse leaves an
63422              * @param {Calendar} this
63423              * @param {event}
63424              */
63425         'eventleave': true,
63426         /**
63427              * @event eventclick
63428              * Fires when the mouse click an
63429              * @param {Calendar} this
63430              * @param {event}
63431              */
63432         'eventclick': true,
63433         /**
63434              * @event eventrender
63435              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63436              * @param {Calendar} this
63437              * @param {data} data to be modified
63438              */
63439         'eventrender': true
63440         
63441     });
63442
63443     Roo.grid.Grid.superclass.constructor.call(this);
63444     this.on('render', function() {
63445         this.view.el.addClass('x-grid-cal'); 
63446         
63447         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63448
63449     },this);
63450     
63451     if (!Roo.grid.Calendar.style) {
63452         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63453             
63454             
63455             '.x-grid-cal .x-grid-col' :  {
63456                 height: 'auto !important',
63457                 'vertical-align': 'top'
63458             },
63459             '.x-grid-cal  .fc-event-hori' : {
63460                 height: '14px'
63461             }
63462              
63463             
63464         }, Roo.id());
63465     }
63466
63467     
63468     
63469 };
63470 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63471     /**
63472      * @cfg {Store} eventStore The store that loads events.
63473      */
63474     eventStore : 25,
63475
63476      
63477     activeDate : false,
63478     startDay : 0,
63479     autoWidth : true,
63480     monitorWindowResize : false,
63481
63482     
63483     resizeColumns : function() {
63484         var col = (this.view.el.getWidth() / 7) - 3;
63485         // loop through cols, and setWidth
63486         for(var i =0 ; i < 7 ; i++){
63487             this.cm.setColumnWidth(i, col);
63488         }
63489     },
63490      setDate :function(date) {
63491         
63492         Roo.log('setDate?');
63493         
63494         this.resizeColumns();
63495         var vd = this.activeDate;
63496         this.activeDate = date;
63497 //        if(vd && this.el){
63498 //            var t = date.getTime();
63499 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63500 //                Roo.log('using add remove');
63501 //                
63502 //                this.fireEvent('monthchange', this, date);
63503 //                
63504 //                this.cells.removeClass("fc-state-highlight");
63505 //                this.cells.each(function(c){
63506 //                   if(c.dateValue == t){
63507 //                       c.addClass("fc-state-highlight");
63508 //                       setTimeout(function(){
63509 //                            try{c.dom.firstChild.focus();}catch(e){}
63510 //                       }, 50);
63511 //                       return false;
63512 //                   }
63513 //                   return true;
63514 //                });
63515 //                return;
63516 //            }
63517 //        }
63518         
63519         var days = date.getDaysInMonth();
63520         
63521         var firstOfMonth = date.getFirstDateOfMonth();
63522         var startingPos = firstOfMonth.getDay()-this.startDay;
63523         
63524         if(startingPos < this.startDay){
63525             startingPos += 7;
63526         }
63527         
63528         var pm = date.add(Date.MONTH, -1);
63529         var prevStart = pm.getDaysInMonth()-startingPos;
63530 //        
63531         
63532         
63533         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63534         
63535         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63536         //this.cells.addClassOnOver('fc-state-hover');
63537         
63538         var cells = this.cells.elements;
63539         var textEls = this.textNodes;
63540         
63541         //Roo.each(cells, function(cell){
63542         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63543         //});
63544         
63545         days += startingPos;
63546
63547         // convert everything to numbers so it's fast
63548         var day = 86400000;
63549         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63550         //Roo.log(d);
63551         //Roo.log(pm);
63552         //Roo.log(prevStart);
63553         
63554         var today = new Date().clearTime().getTime();
63555         var sel = date.clearTime().getTime();
63556         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63557         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63558         var ddMatch = this.disabledDatesRE;
63559         var ddText = this.disabledDatesText;
63560         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63561         var ddaysText = this.disabledDaysText;
63562         var format = this.format;
63563         
63564         var setCellClass = function(cal, cell){
63565             
63566             //Roo.log('set Cell Class');
63567             cell.title = "";
63568             var t = d.getTime();
63569             
63570             //Roo.log(d);
63571             
63572             
63573             cell.dateValue = t;
63574             if(t == today){
63575                 cell.className += " fc-today";
63576                 cell.className += " fc-state-highlight";
63577                 cell.title = cal.todayText;
63578             }
63579             if(t == sel){
63580                 // disable highlight in other month..
63581                 cell.className += " fc-state-highlight";
63582                 
63583             }
63584             // disabling
63585             if(t < min) {
63586                 //cell.className = " fc-state-disabled";
63587                 cell.title = cal.minText;
63588                 return;
63589             }
63590             if(t > max) {
63591                 //cell.className = " fc-state-disabled";
63592                 cell.title = cal.maxText;
63593                 return;
63594             }
63595             if(ddays){
63596                 if(ddays.indexOf(d.getDay()) != -1){
63597                     // cell.title = ddaysText;
63598                    // cell.className = " fc-state-disabled";
63599                 }
63600             }
63601             if(ddMatch && format){
63602                 var fvalue = d.dateFormat(format);
63603                 if(ddMatch.test(fvalue)){
63604                     cell.title = ddText.replace("%0", fvalue);
63605                    cell.className = " fc-state-disabled";
63606                 }
63607             }
63608             
63609             if (!cell.initialClassName) {
63610                 cell.initialClassName = cell.dom.className;
63611             }
63612             
63613             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63614         };
63615
63616         var i = 0;
63617         
63618         for(; i < startingPos; i++) {
63619             cells[i].dayName =  (++prevStart);
63620             Roo.log(textEls[i]);
63621             d.setDate(d.getDate()+1);
63622             
63623             //cells[i].className = "fc-past fc-other-month";
63624             setCellClass(this, cells[i]);
63625         }
63626         
63627         var intDay = 0;
63628         
63629         for(; i < days; i++){
63630             intDay = i - startingPos + 1;
63631             cells[i].dayName =  (intDay);
63632             d.setDate(d.getDate()+1);
63633             
63634             cells[i].className = ''; // "x-date-active";
63635             setCellClass(this, cells[i]);
63636         }
63637         var extraDays = 0;
63638         
63639         for(; i < 42; i++) {
63640             //textEls[i].innerHTML = (++extraDays);
63641             
63642             d.setDate(d.getDate()+1);
63643             cells[i].dayName = (++extraDays);
63644             cells[i].className = "fc-future fc-other-month";
63645             setCellClass(this, cells[i]);
63646         }
63647         
63648         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63649         
63650         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63651         
63652         // this will cause all the cells to mis
63653         var rows= [];
63654         var i =0;
63655         for (var r = 0;r < 6;r++) {
63656             for (var c =0;c < 7;c++) {
63657                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63658             }    
63659         }
63660         
63661         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63662         for(i=0;i<cells.length;i++) {
63663             
63664             this.cells.elements[i].dayName = cells[i].dayName ;
63665             this.cells.elements[i].className = cells[i].className;
63666             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63667             this.cells.elements[i].title = cells[i].title ;
63668             this.cells.elements[i].dateValue = cells[i].dateValue ;
63669         }
63670         
63671         
63672         
63673         
63674         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63675         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63676         
63677         ////if(totalRows != 6){
63678             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63679            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63680        // }
63681         
63682         this.fireEvent('monthchange', this, date);
63683         
63684         
63685     },
63686  /**
63687      * Returns the grid's SelectionModel.
63688      * @return {SelectionModel}
63689      */
63690     getSelectionModel : function(){
63691         if(!this.selModel){
63692             this.selModel = new Roo.grid.CellSelectionModel();
63693         }
63694         return this.selModel;
63695     },
63696
63697     load: function() {
63698         this.eventStore.load()
63699         
63700         
63701         
63702     },
63703     
63704     findCell : function(dt) {
63705         dt = dt.clearTime().getTime();
63706         var ret = false;
63707         this.cells.each(function(c){
63708             //Roo.log("check " +c.dateValue + '?=' + dt);
63709             if(c.dateValue == dt){
63710                 ret = c;
63711                 return false;
63712             }
63713             return true;
63714         });
63715         
63716         return ret;
63717     },
63718     
63719     findCells : function(rec) {
63720         var s = rec.data.start_dt.clone().clearTime().getTime();
63721        // Roo.log(s);
63722         var e= rec.data.end_dt.clone().clearTime().getTime();
63723        // Roo.log(e);
63724         var ret = [];
63725         this.cells.each(function(c){
63726              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63727             
63728             if(c.dateValue > e){
63729                 return ;
63730             }
63731             if(c.dateValue < s){
63732                 return ;
63733             }
63734             ret.push(c);
63735         });
63736         
63737         return ret;    
63738     },
63739     
63740     findBestRow: function(cells)
63741     {
63742         var ret = 0;
63743         
63744         for (var i =0 ; i < cells.length;i++) {
63745             ret  = Math.max(cells[i].rows || 0,ret);
63746         }
63747         return ret;
63748         
63749     },
63750     
63751     
63752     addItem : function(rec)
63753     {
63754         // look for vertical location slot in
63755         var cells = this.findCells(rec);
63756         
63757         rec.row = this.findBestRow(cells);
63758         
63759         // work out the location.
63760         
63761         var crow = false;
63762         var rows = [];
63763         for(var i =0; i < cells.length; i++) {
63764             if (!crow) {
63765                 crow = {
63766                     start : cells[i],
63767                     end :  cells[i]
63768                 };
63769                 continue;
63770             }
63771             if (crow.start.getY() == cells[i].getY()) {
63772                 // on same row.
63773                 crow.end = cells[i];
63774                 continue;
63775             }
63776             // different row.
63777             rows.push(crow);
63778             crow = {
63779                 start: cells[i],
63780                 end : cells[i]
63781             };
63782             
63783         }
63784         
63785         rows.push(crow);
63786         rec.els = [];
63787         rec.rows = rows;
63788         rec.cells = cells;
63789         for (var i = 0; i < cells.length;i++) {
63790             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63791             
63792         }
63793         
63794         
63795     },
63796     
63797     clearEvents: function() {
63798         
63799         if (!this.eventStore.getCount()) {
63800             return;
63801         }
63802         // reset number of rows in cells.
63803         Roo.each(this.cells.elements, function(c){
63804             c.rows = 0;
63805         });
63806         
63807         this.eventStore.each(function(e) {
63808             this.clearEvent(e);
63809         },this);
63810         
63811     },
63812     
63813     clearEvent : function(ev)
63814     {
63815         if (ev.els) {
63816             Roo.each(ev.els, function(el) {
63817                 el.un('mouseenter' ,this.onEventEnter, this);
63818                 el.un('mouseleave' ,this.onEventLeave, this);
63819                 el.remove();
63820             },this);
63821             ev.els = [];
63822         }
63823     },
63824     
63825     
63826     renderEvent : function(ev,ctr) {
63827         if (!ctr) {
63828              ctr = this.view.el.select('.fc-event-container',true).first();
63829         }
63830         
63831          
63832         this.clearEvent(ev);
63833             //code
63834        
63835         
63836         
63837         ev.els = [];
63838         var cells = ev.cells;
63839         var rows = ev.rows;
63840         this.fireEvent('eventrender', this, ev);
63841         
63842         for(var i =0; i < rows.length; i++) {
63843             
63844             cls = '';
63845             if (i == 0) {
63846                 cls += ' fc-event-start';
63847             }
63848             if ((i+1) == rows.length) {
63849                 cls += ' fc-event-end';
63850             }
63851             
63852             //Roo.log(ev.data);
63853             // how many rows should it span..
63854             var cg = this.eventTmpl.append(ctr,Roo.apply({
63855                 fccls : cls
63856                 
63857             }, ev.data) , true);
63858             
63859             
63860             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63861             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63862             cg.on('click', this.onEventClick, this, ev);
63863             
63864             ev.els.push(cg);
63865             
63866             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63867             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63868             //Roo.log(cg);
63869              
63870             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63871             cg.setWidth(ebox.right - sbox.x -2);
63872         }
63873     },
63874     
63875     renderEvents: function()
63876     {   
63877         // first make sure there is enough space..
63878         
63879         if (!this.eventTmpl) {
63880             this.eventTmpl = new Roo.Template(
63881                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63882                     '<div class="fc-event-inner">' +
63883                         '<span class="fc-event-time">{time}</span>' +
63884                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63885                     '</div>' +
63886                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63887                 '</div>'
63888             );
63889                 
63890         }
63891                
63892         
63893         
63894         this.cells.each(function(c) {
63895             //Roo.log(c.select('.fc-day-content div',true).first());
63896             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63897         });
63898         
63899         var ctr = this.view.el.select('.fc-event-container',true).first();
63900         
63901         var cls;
63902         this.eventStore.each(function(ev){
63903             
63904             this.renderEvent(ev);
63905              
63906              
63907         }, this);
63908         this.view.layout();
63909         
63910     },
63911     
63912     onEventEnter: function (e, el,event,d) {
63913         this.fireEvent('evententer', this, el, event);
63914     },
63915     
63916     onEventLeave: function (e, el,event,d) {
63917         this.fireEvent('eventleave', this, el, event);
63918     },
63919     
63920     onEventClick: function (e, el,event,d) {
63921         this.fireEvent('eventclick', this, el, event);
63922     },
63923     
63924     onMonthChange: function () {
63925         this.store.load();
63926     },
63927     
63928     onLoad: function () {
63929         
63930         //Roo.log('calendar onload');
63931 //         
63932         if(this.eventStore.getCount() > 0){
63933             
63934            
63935             
63936             this.eventStore.each(function(d){
63937                 
63938                 
63939                 // FIXME..
63940                 var add =   d.data;
63941                 if (typeof(add.end_dt) == 'undefined')  {
63942                     Roo.log("Missing End time in calendar data: ");
63943                     Roo.log(d);
63944                     return;
63945                 }
63946                 if (typeof(add.start_dt) == 'undefined')  {
63947                     Roo.log("Missing Start time in calendar data: ");
63948                     Roo.log(d);
63949                     return;
63950                 }
63951                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63952                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63953                 add.id = add.id || d.id;
63954                 add.title = add.title || '??';
63955                 
63956                 this.addItem(d);
63957                 
63958              
63959             },this);
63960         }
63961         
63962         this.renderEvents();
63963     }
63964     
63965
63966 });
63967 /*
63968  grid : {
63969                 xtype: 'Grid',
63970                 xns: Roo.grid,
63971                 listeners : {
63972                     render : function ()
63973                     {
63974                         _this.grid = this;
63975                         
63976                         if (!this.view.el.hasClass('course-timesheet')) {
63977                             this.view.el.addClass('course-timesheet');
63978                         }
63979                         if (this.tsStyle) {
63980                             this.ds.load({});
63981                             return; 
63982                         }
63983                         Roo.log('width');
63984                         Roo.log(_this.grid.view.el.getWidth());
63985                         
63986                         
63987                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63988                             '.course-timesheet .x-grid-row' : {
63989                                 height: '80px'
63990                             },
63991                             '.x-grid-row td' : {
63992                                 'vertical-align' : 0
63993                             },
63994                             '.course-edit-link' : {
63995                                 'color' : 'blue',
63996                                 'text-overflow' : 'ellipsis',
63997                                 'overflow' : 'hidden',
63998                                 'white-space' : 'nowrap',
63999                                 'cursor' : 'pointer'
64000                             },
64001                             '.sub-link' : {
64002                                 'color' : 'green'
64003                             },
64004                             '.de-act-sup-link' : {
64005                                 'color' : 'purple',
64006                                 'text-decoration' : 'line-through'
64007                             },
64008                             '.de-act-link' : {
64009                                 'color' : 'red',
64010                                 'text-decoration' : 'line-through'
64011                             },
64012                             '.course-timesheet .course-highlight' : {
64013                                 'border-top-style': 'dashed !important',
64014                                 'border-bottom-bottom': 'dashed !important'
64015                             },
64016                             '.course-timesheet .course-item' : {
64017                                 'font-family'   : 'tahoma, arial, helvetica',
64018                                 'font-size'     : '11px',
64019                                 'overflow'      : 'hidden',
64020                                 'padding-left'  : '10px',
64021                                 'padding-right' : '10px',
64022                                 'padding-top' : '10px' 
64023                             }
64024                             
64025                         }, Roo.id());
64026                                 this.ds.load({});
64027                     }
64028                 },
64029                 autoWidth : true,
64030                 monitorWindowResize : false,
64031                 cellrenderer : function(v,x,r)
64032                 {
64033                     return v;
64034                 },
64035                 sm : {
64036                     xtype: 'CellSelectionModel',
64037                     xns: Roo.grid
64038                 },
64039                 dataSource : {
64040                     xtype: 'Store',
64041                     xns: Roo.data,
64042                     listeners : {
64043                         beforeload : function (_self, options)
64044                         {
64045                             options.params = options.params || {};
64046                             options.params._month = _this.monthField.getValue();
64047                             options.params.limit = 9999;
64048                             options.params['sort'] = 'when_dt';    
64049                             options.params['dir'] = 'ASC';    
64050                             this.proxy.loadResponse = this.loadResponse;
64051                             Roo.log("load?");
64052                             //this.addColumns();
64053                         },
64054                         load : function (_self, records, options)
64055                         {
64056                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64057                                 // if you click on the translation.. you can edit it...
64058                                 var el = Roo.get(this);
64059                                 var id = el.dom.getAttribute('data-id');
64060                                 var d = el.dom.getAttribute('data-date');
64061                                 var t = el.dom.getAttribute('data-time');
64062                                 //var id = this.child('span').dom.textContent;
64063                                 
64064                                 //Roo.log(this);
64065                                 Pman.Dialog.CourseCalendar.show({
64066                                     id : id,
64067                                     when_d : d,
64068                                     when_t : t,
64069                                     productitem_active : id ? 1 : 0
64070                                 }, function() {
64071                                     _this.grid.ds.load({});
64072                                 });
64073                            
64074                            });
64075                            
64076                            _this.panel.fireEvent('resize', [ '', '' ]);
64077                         }
64078                     },
64079                     loadResponse : function(o, success, response){
64080                             // this is overridden on before load..
64081                             
64082                             Roo.log("our code?");       
64083                             //Roo.log(success);
64084                             //Roo.log(response)
64085                             delete this.activeRequest;
64086                             if(!success){
64087                                 this.fireEvent("loadexception", this, o, response);
64088                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64089                                 return;
64090                             }
64091                             var result;
64092                             try {
64093                                 result = o.reader.read(response);
64094                             }catch(e){
64095                                 Roo.log("load exception?");
64096                                 this.fireEvent("loadexception", this, o, response, e);
64097                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64098                                 return;
64099                             }
64100                             Roo.log("ready...");        
64101                             // loop through result.records;
64102                             // and set this.tdate[date] = [] << array of records..
64103                             _this.tdata  = {};
64104                             Roo.each(result.records, function(r){
64105                                 //Roo.log(r.data);
64106                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64107                                     _this.tdata[r.data.when_dt.format('j')] = [];
64108                                 }
64109                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64110                             });
64111                             
64112                             //Roo.log(_this.tdata);
64113                             
64114                             result.records = [];
64115                             result.totalRecords = 6;
64116                     
64117                             // let's generate some duumy records for the rows.
64118                             //var st = _this.dateField.getValue();
64119                             
64120                             // work out monday..
64121                             //st = st.add(Date.DAY, -1 * st.format('w'));
64122                             
64123                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64124                             
64125                             var firstOfMonth = date.getFirstDayOfMonth();
64126                             var days = date.getDaysInMonth();
64127                             var d = 1;
64128                             var firstAdded = false;
64129                             for (var i = 0; i < result.totalRecords ; i++) {
64130                                 //var d= st.add(Date.DAY, i);
64131                                 var row = {};
64132                                 var added = 0;
64133                                 for(var w = 0 ; w < 7 ; w++){
64134                                     if(!firstAdded && firstOfMonth != w){
64135                                         continue;
64136                                     }
64137                                     if(d > days){
64138                                         continue;
64139                                     }
64140                                     firstAdded = true;
64141                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64142                                     row['weekday'+w] = String.format(
64143                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64144                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64145                                                     d,
64146                                                     date.format('Y-m-')+dd
64147                                                 );
64148                                     added++;
64149                                     if(typeof(_this.tdata[d]) != 'undefined'){
64150                                         Roo.each(_this.tdata[d], function(r){
64151                                             var is_sub = '';
64152                                             var deactive = '';
64153                                             var id = r.id;
64154                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64155                                             if(r.parent_id*1>0){
64156                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64157                                                 id = r.parent_id;
64158                                             }
64159                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64160                                                 deactive = 'de-act-link';
64161                                             }
64162                                             
64163                                             row['weekday'+w] += String.format(
64164                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64165                                                     id, //0
64166                                                     r.product_id_name, //1
64167                                                     r.when_dt.format('h:ia'), //2
64168                                                     is_sub, //3
64169                                                     deactive, //4
64170                                                     desc // 5
64171                                             );
64172                                         });
64173                                     }
64174                                     d++;
64175                                 }
64176                                 
64177                                 // only do this if something added..
64178                                 if(added > 0){ 
64179                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64180                                 }
64181                                 
64182                                 
64183                                 // push it twice. (second one with an hour..
64184                                 
64185                             }
64186                             //Roo.log(result);
64187                             this.fireEvent("load", this, o, o.request.arg);
64188                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64189                         },
64190                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64191                     proxy : {
64192                         xtype: 'HttpProxy',
64193                         xns: Roo.data,
64194                         method : 'GET',
64195                         url : baseURL + '/Roo/Shop_course.php'
64196                     },
64197                     reader : {
64198                         xtype: 'JsonReader',
64199                         xns: Roo.data,
64200                         id : 'id',
64201                         fields : [
64202                             {
64203                                 'name': 'id',
64204                                 'type': 'int'
64205                             },
64206                             {
64207                                 'name': 'when_dt',
64208                                 'type': 'string'
64209                             },
64210                             {
64211                                 'name': 'end_dt',
64212                                 'type': 'string'
64213                             },
64214                             {
64215                                 'name': 'parent_id',
64216                                 'type': 'int'
64217                             },
64218                             {
64219                                 'name': 'product_id',
64220                                 'type': 'int'
64221                             },
64222                             {
64223                                 'name': 'productitem_id',
64224                                 'type': 'int'
64225                             },
64226                             {
64227                                 'name': 'guid',
64228                                 'type': 'int'
64229                             }
64230                         ]
64231                     }
64232                 },
64233                 toolbar : {
64234                     xtype: 'Toolbar',
64235                     xns: Roo,
64236                     items : [
64237                         {
64238                             xtype: 'Button',
64239                             xns: Roo.Toolbar,
64240                             listeners : {
64241                                 click : function (_self, e)
64242                                 {
64243                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64244                                     sd.setMonth(sd.getMonth()-1);
64245                                     _this.monthField.setValue(sd.format('Y-m-d'));
64246                                     _this.grid.ds.load({});
64247                                 }
64248                             },
64249                             text : "Back"
64250                         },
64251                         {
64252                             xtype: 'Separator',
64253                             xns: Roo.Toolbar
64254                         },
64255                         {
64256                             xtype: 'MonthField',
64257                             xns: Roo.form,
64258                             listeners : {
64259                                 render : function (_self)
64260                                 {
64261                                     _this.monthField = _self;
64262                                    // _this.monthField.set  today
64263                                 },
64264                                 select : function (combo, date)
64265                                 {
64266                                     _this.grid.ds.load({});
64267                                 }
64268                             },
64269                             value : (function() { return new Date(); })()
64270                         },
64271                         {
64272                             xtype: 'Separator',
64273                             xns: Roo.Toolbar
64274                         },
64275                         {
64276                             xtype: 'TextItem',
64277                             xns: Roo.Toolbar,
64278                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64279                         },
64280                         {
64281                             xtype: 'Fill',
64282                             xns: Roo.Toolbar
64283                         },
64284                         {
64285                             xtype: 'Button',
64286                             xns: Roo.Toolbar,
64287                             listeners : {
64288                                 click : function (_self, e)
64289                                 {
64290                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64291                                     sd.setMonth(sd.getMonth()+1);
64292                                     _this.monthField.setValue(sd.format('Y-m-d'));
64293                                     _this.grid.ds.load({});
64294                                 }
64295                             },
64296                             text : "Next"
64297                         }
64298                     ]
64299                 },
64300                  
64301             }
64302         };
64303         
64304         *//*
64305  * Based on:
64306  * Ext JS Library 1.1.1
64307  * Copyright(c) 2006-2007, Ext JS, LLC.
64308  *
64309  * Originally Released Under LGPL - original licence link has changed is not relivant.
64310  *
64311  * Fork - LGPL
64312  * <script type="text/javascript">
64313  */
64314  
64315 /**
64316  * @class Roo.LoadMask
64317  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64318  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64319  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64320  * element's UpdateManager load indicator and will be destroyed after the initial load.
64321  * @constructor
64322  * Create a new LoadMask
64323  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64324  * @param {Object} config The config object
64325  */
64326 Roo.LoadMask = function(el, config){
64327     this.el = Roo.get(el);
64328     Roo.apply(this, config);
64329     if(this.store){
64330         this.store.on('beforeload', this.onBeforeLoad, this);
64331         this.store.on('load', this.onLoad, this);
64332         this.store.on('loadexception', this.onLoadException, this);
64333         this.removeMask = false;
64334     }else{
64335         var um = this.el.getUpdateManager();
64336         um.showLoadIndicator = false; // disable the default indicator
64337         um.on('beforeupdate', this.onBeforeLoad, this);
64338         um.on('update', this.onLoad, this);
64339         um.on('failure', this.onLoad, this);
64340         this.removeMask = true;
64341     }
64342 };
64343
64344 Roo.LoadMask.prototype = {
64345     /**
64346      * @cfg {Boolean} removeMask
64347      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64348      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64349      */
64350     removeMask : false,
64351     /**
64352      * @cfg {String} msg
64353      * The text to display in a centered loading message box (defaults to 'Loading...')
64354      */
64355     msg : 'Loading...',
64356     /**
64357      * @cfg {String} msgCls
64358      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64359      */
64360     msgCls : 'x-mask-loading',
64361
64362     /**
64363      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64364      * @type Boolean
64365      */
64366     disabled: false,
64367
64368     /**
64369      * Disables the mask to prevent it from being displayed
64370      */
64371     disable : function(){
64372        this.disabled = true;
64373     },
64374
64375     /**
64376      * Enables the mask so that it can be displayed
64377      */
64378     enable : function(){
64379         this.disabled = false;
64380     },
64381     
64382     onLoadException : function()
64383     {
64384         Roo.log(arguments);
64385         
64386         if (typeof(arguments[3]) != 'undefined') {
64387             Roo.MessageBox.alert("Error loading",arguments[3]);
64388         } 
64389         /*
64390         try {
64391             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64392                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64393             }   
64394         } catch(e) {
64395             
64396         }
64397         */
64398     
64399         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64400     },
64401     // private
64402     onLoad : function()
64403     {
64404         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64405     },
64406
64407     // private
64408     onBeforeLoad : function(){
64409         if(!this.disabled){
64410             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64411         }
64412     },
64413
64414     // private
64415     destroy : function(){
64416         if(this.store){
64417             this.store.un('beforeload', this.onBeforeLoad, this);
64418             this.store.un('load', this.onLoad, this);
64419             this.store.un('loadexception', this.onLoadException, this);
64420         }else{
64421             var um = this.el.getUpdateManager();
64422             um.un('beforeupdate', this.onBeforeLoad, this);
64423             um.un('update', this.onLoad, this);
64424             um.un('failure', this.onLoad, this);
64425         }
64426     }
64427 };/*
64428  * Based on:
64429  * Ext JS Library 1.1.1
64430  * Copyright(c) 2006-2007, Ext JS, LLC.
64431  *
64432  * Originally Released Under LGPL - original licence link has changed is not relivant.
64433  *
64434  * Fork - LGPL
64435  * <script type="text/javascript">
64436  */
64437
64438
64439 /**
64440  * @class Roo.XTemplate
64441  * @extends Roo.Template
64442  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64443 <pre><code>
64444 var t = new Roo.XTemplate(
64445         '&lt;select name="{name}"&gt;',
64446                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64447         '&lt;/select&gt;'
64448 );
64449  
64450 // then append, applying the master template values
64451  </code></pre>
64452  *
64453  * Supported features:
64454  *
64455  *  Tags:
64456
64457 <pre><code>
64458       {a_variable} - output encoded.
64459       {a_variable.format:("Y-m-d")} - call a method on the variable
64460       {a_variable:raw} - unencoded output
64461       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64462       {a_variable:this.method_on_template(...)} - call a method on the template object.
64463  
64464 </code></pre>
64465  *  The tpl tag:
64466 <pre><code>
64467         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64468         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64469         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64470         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64471   
64472         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64473         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64474 </code></pre>
64475  *      
64476  */
64477 Roo.XTemplate = function()
64478 {
64479     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64480     if (this.html) {
64481         this.compile();
64482     }
64483 };
64484
64485
64486 Roo.extend(Roo.XTemplate, Roo.Template, {
64487
64488     /**
64489      * The various sub templates
64490      */
64491     tpls : false,
64492     /**
64493      *
64494      * basic tag replacing syntax
64495      * WORD:WORD()
64496      *
64497      * // you can fake an object call by doing this
64498      *  x.t:(test,tesT) 
64499      * 
64500      */
64501     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64502
64503     /**
64504      * compile the template
64505      *
64506      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64507      *
64508      */
64509     compile: function()
64510     {
64511         var s = this.html;
64512      
64513         s = ['<tpl>', s, '</tpl>'].join('');
64514     
64515         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64516             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64517             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64518             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64519             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64520             m,
64521             id     = 0,
64522             tpls   = [];
64523     
64524         while(true == !!(m = s.match(re))){
64525             var forMatch   = m[0].match(nameRe),
64526                 ifMatch   = m[0].match(ifRe),
64527                 execMatch   = m[0].match(execRe),
64528                 namedMatch   = m[0].match(namedRe),
64529                 
64530                 exp  = null, 
64531                 fn   = null,
64532                 exec = null,
64533                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64534                 
64535             if (ifMatch) {
64536                 // if - puts fn into test..
64537                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64538                 if(exp){
64539                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64540                 }
64541             }
64542             
64543             if (execMatch) {
64544                 // exec - calls a function... returns empty if true is  returned.
64545                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64546                 if(exp){
64547                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64548                 }
64549             }
64550             
64551             
64552             if (name) {
64553                 // for = 
64554                 switch(name){
64555                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64556                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64557                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64558                 }
64559             }
64560             var uid = namedMatch ? namedMatch[1] : id;
64561             
64562             
64563             tpls.push({
64564                 id:     namedMatch ? namedMatch[1] : id,
64565                 target: name,
64566                 exec:   exec,
64567                 test:   fn,
64568                 body:   m[1] || ''
64569             });
64570             if (namedMatch) {
64571                 s = s.replace(m[0], '');
64572             } else { 
64573                 s = s.replace(m[0], '{xtpl'+ id + '}');
64574             }
64575             ++id;
64576         }
64577         this.tpls = [];
64578         for(var i = tpls.length-1; i >= 0; --i){
64579             this.compileTpl(tpls[i]);
64580             this.tpls[tpls[i].id] = tpls[i];
64581         }
64582         this.master = tpls[tpls.length-1];
64583         return this;
64584     },
64585     /**
64586      * same as applyTemplate, except it's done to one of the subTemplates
64587      * when using named templates, you can do:
64588      *
64589      * var str = pl.applySubTemplate('your-name', values);
64590      *
64591      * 
64592      * @param {Number} id of the template
64593      * @param {Object} values to apply to template
64594      * @param {Object} parent (normaly the instance of this object)
64595      */
64596     applySubTemplate : function(id, values, parent)
64597     {
64598         
64599         
64600         var t = this.tpls[id];
64601         
64602         
64603         try { 
64604             if(t.test && !t.test.call(this, values, parent)){
64605                 return '';
64606             }
64607         } catch(e) {
64608             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64609             Roo.log(e.toString());
64610             Roo.log(t.test);
64611             return ''
64612         }
64613         try { 
64614             
64615             if(t.exec && t.exec.call(this, values, parent)){
64616                 return '';
64617             }
64618         } catch(e) {
64619             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64620             Roo.log(e.toString());
64621             Roo.log(t.exec);
64622             return ''
64623         }
64624         try {
64625             var vs = t.target ? t.target.call(this, values, parent) : values;
64626             parent = t.target ? values : parent;
64627             if(t.target && vs instanceof Array){
64628                 var buf = [];
64629                 for(var i = 0, len = vs.length; i < len; i++){
64630                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64631                 }
64632                 return buf.join('');
64633             }
64634             return t.compiled.call(this, vs, parent);
64635         } catch (e) {
64636             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64637             Roo.log(e.toString());
64638             Roo.log(t.compiled);
64639             return '';
64640         }
64641     },
64642
64643     compileTpl : function(tpl)
64644     {
64645         var fm = Roo.util.Format;
64646         var useF = this.disableFormats !== true;
64647         var sep = Roo.isGecko ? "+" : ",";
64648         var undef = function(str) {
64649             Roo.log("Property not found :"  + str);
64650             return '';
64651         };
64652         
64653         var fn = function(m, name, format, args)
64654         {
64655             //Roo.log(arguments);
64656             args = args ? args.replace(/\\'/g,"'") : args;
64657             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64658             if (typeof(format) == 'undefined') {
64659                 format= 'htmlEncode';
64660             }
64661             if (format == 'raw' ) {
64662                 format = false;
64663             }
64664             
64665             if(name.substr(0, 4) == 'xtpl'){
64666                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64667             }
64668             
64669             // build an array of options to determine if value is undefined..
64670             
64671             // basically get 'xxxx.yyyy' then do
64672             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64673             //    (function () { Roo.log("Property not found"); return ''; })() :
64674             //    ......
64675             
64676             var udef_ar = [];
64677             var lookfor = '';
64678             Roo.each(name.split('.'), function(st) {
64679                 lookfor += (lookfor.length ? '.': '') + st;
64680                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64681             });
64682             
64683             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64684             
64685             
64686             if(format && useF){
64687                 
64688                 args = args ? ',' + args : "";
64689                  
64690                 if(format.substr(0, 5) != "this."){
64691                     format = "fm." + format + '(';
64692                 }else{
64693                     format = 'this.call("'+ format.substr(5) + '", ';
64694                     args = ", values";
64695                 }
64696                 
64697                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64698             }
64699              
64700             if (args.length) {
64701                 // called with xxyx.yuu:(test,test)
64702                 // change to ()
64703                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64704             }
64705             // raw.. - :raw modifier..
64706             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64707             
64708         };
64709         var body;
64710         // branched to use + in gecko and [].join() in others
64711         if(Roo.isGecko){
64712             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64713                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64714                     "';};};";
64715         }else{
64716             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64717             body.push(tpl.body.replace(/(\r\n|\n)/g,
64718                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64719             body.push("'].join('');};};");
64720             body = body.join('');
64721         }
64722         
64723         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64724        
64725         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64726         eval(body);
64727         
64728         return this;
64729     },
64730
64731     applyTemplate : function(values){
64732         return this.master.compiled.call(this, values, {});
64733         //var s = this.subs;
64734     },
64735
64736     apply : function(){
64737         return this.applyTemplate.apply(this, arguments);
64738     }
64739
64740  });
64741
64742 Roo.XTemplate.from = function(el){
64743     el = Roo.getDom(el);
64744     return new Roo.XTemplate(el.value || el.innerHTML);
64745 };