roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux" 
704                );
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  * Based on:
959  * Ext JS Library 1.1.1
960  * Copyright(c) 2006-2007, Ext JS, LLC.
961  *
962  * Originally Released Under LGPL - original licence link has changed is not relivant.
963  *
964  * Fork - LGPL
965  * <script type="text/javascript">
966  */
967
968  /**
969  * @class Number
970  */
971 Roo.applyIf(Number.prototype, {
972     /**
973      * Checks whether or not the current number is within a desired range.  If the number is already within the
974      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
975      * exceeded.  Note that this method returns the constrained value but does not change the current number.
976      * @param {Number} min The minimum number in the range
977      * @param {Number} max The maximum number in the range
978      * @return {Number} The constrained value if outside the range, otherwise the current value
979      */
980     constrain : function(min, max){
981         return Math.min(Math.max(this, min), max);
982     }
983 });/*
984  * Based on:
985  * Ext JS Library 1.1.1
986  * Copyright(c) 2006-2007, Ext JS, LLC.
987  *
988  * Originally Released Under LGPL - original licence link has changed is not relivant.
989  *
990  * Fork - LGPL
991  * <script type="text/javascript">
992  */
993  /**
994  * @class Array
995  */
996 Roo.applyIf(Array.prototype, {
997     /**
998      * 
999      * Checks whether or not the specified object exists in the array.
1000      * @param {Object} o The object to check for
1001      * @return {Number} The index of o in the array (or -1 if it is not found)
1002      */
1003     indexOf : function(o){
1004        for (var i = 0, len = this.length; i < len; i++){
1005               if(this[i] == o) { return i; }
1006        }
1007            return -1;
1008     },
1009
1010     /**
1011      * Removes the specified object from the array.  If the object is not found nothing happens.
1012      * @param {Object} o The object to remove
1013      */
1014     remove : function(o){
1015        var index = this.indexOf(o);
1016        if(index != -1){
1017            this.splice(index, 1);
1018        }
1019     },
1020     /**
1021      * Map (JS 1.6 compatibility)
1022      * @param {Function} function  to call
1023      */
1024     map : function(fun )
1025     {
1026         var len = this.length >>> 0;
1027         if (typeof fun != "function") {
1028             throw new TypeError();
1029         }
1030         var res = new Array(len);
1031         var thisp = arguments[1];
1032         for (var i = 0; i < len; i++)
1033         {
1034             if (i in this) {
1035                 res[i] = fun.call(thisp, this[i], i, this);
1036             }
1037         }
1038
1039         return res;
1040     },
1041     /**
1042      * equals
1043      * @param {Array} o The array to compare to
1044      * @returns {Boolean} true if the same
1045      */
1046     equals : function(b)
1047     {
1048             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1049         if (this === b) {
1050             return true;
1051         }
1052         if (b == null) {
1053             return false;
1054         }
1055         if (this.length !== b.length) {
1056             return false;
1057         }
1058           
1059         // sort?? a.sort().equals(b.sort());
1060           
1061         for (var i = 0; i < this.length; ++i) {
1062             if (this[i] !== b[i]) {
1063             return false;
1064             }
1065         }
1066         return true;
1067     } 
1068     
1069     
1070     
1071     
1072 });
1073
1074 Roo.applyIf(Array, {
1075  /**
1076      * from
1077      * @static
1078      * @param {Array} o Or Array like object (eg. nodelist)
1079      * @returns {Array} 
1080      */
1081     from : function(o)
1082     {
1083         var ret= [];
1084     
1085         for (var i =0; i < o.length; i++) { 
1086             ret[i] = o[i];
1087         }
1088         return ret;
1089       
1090     }
1091 });
1092 /*
1093  * Based on:
1094  * Ext JS Library 1.1.1
1095  * Copyright(c) 2006-2007, Ext JS, LLC.
1096  *
1097  * Originally Released Under LGPL - original licence link has changed is not relivant.
1098  *
1099  * Fork - LGPL
1100  * <script type="text/javascript">
1101  */
1102
1103 /**
1104  * @class Date
1105  *
1106  * The date parsing and format syntax is a subset of
1107  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1108  * supported will provide results equivalent to their PHP versions.
1109  *
1110  * Following is the list of all currently supported formats:
1111  *<pre>
1112 Sample date:
1113 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1114
1115 Format  Output      Description
1116 ------  ----------  --------------------------------------------------------------
1117   d      10         Day of the month, 2 digits with leading zeros
1118   D      Wed        A textual representation of a day, three letters
1119   j      10         Day of the month without leading zeros
1120   l      Wednesday  A full textual representation of the day of the week
1121   S      th         English ordinal day of month suffix, 2 chars (use with j)
1122   w      3          Numeric representation of the day of the week
1123   z      9          The julian date, or day of the year (0-365)
1124   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1125   F      January    A full textual representation of the month
1126   m      01         Numeric representation of a month, with leading zeros
1127   M      Jan        Month name abbreviation, three letters
1128   n      1          Numeric representation of a month, without leading zeros
1129   t      31         Number of days in the given month
1130   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1131   Y      2007       A full numeric representation of a year, 4 digits
1132   y      07         A two digit representation of a year
1133   a      pm         Lowercase Ante meridiem and Post meridiem
1134   A      PM         Uppercase Ante meridiem and Post meridiem
1135   g      3          12-hour format of an hour without leading zeros
1136   G      15         24-hour format of an hour without leading zeros
1137   h      03         12-hour format of an hour with leading zeros
1138   H      15         24-hour format of an hour with leading zeros
1139   i      05         Minutes with leading zeros
1140   s      01         Seconds, with leading zeros
1141   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1142   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1143   T      CST        Timezone setting of the machine running the code
1144   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1145 </pre>
1146  *
1147  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1148  * <pre><code>
1149 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1150 document.write(dt.format('Y-m-d'));                         //2007-01-10
1151 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1152 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
1153  </code></pre>
1154  *
1155  * Here are some standard date/time patterns that you might find helpful.  They
1156  * are not part of the source of Date.js, but to use them you can simply copy this
1157  * block of code into any script that is included after Date.js and they will also become
1158  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1159  * <pre><code>
1160 Date.patterns = {
1161     ISO8601Long:"Y-m-d H:i:s",
1162     ISO8601Short:"Y-m-d",
1163     ShortDate: "n/j/Y",
1164     LongDate: "l, F d, Y",
1165     FullDateTime: "l, F d, Y g:i:s A",
1166     MonthDay: "F d",
1167     ShortTime: "g:i A",
1168     LongTime: "g:i:s A",
1169     SortableDateTime: "Y-m-d\\TH:i:s",
1170     UniversalSortableDateTime: "Y-m-d H:i:sO",
1171     YearMonth: "F, Y"
1172 };
1173 </code></pre>
1174  *
1175  * Example usage:
1176  * <pre><code>
1177 var dt = new Date();
1178 document.write(dt.format(Date.patterns.ShortDate));
1179  </code></pre>
1180  */
1181
1182 /*
1183  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1184  * They generate precompiled functions from date formats instead of parsing and
1185  * processing the pattern every time you format a date.  These functions are available
1186  * on every Date object (any javascript function).
1187  *
1188  * The original article and download are here:
1189  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1190  *
1191  */
1192  
1193  
1194  // was in core
1195 /**
1196  Returns the number of milliseconds between this date and date
1197  @param {Date} date (optional) Defaults to now
1198  @return {Number} The diff in milliseconds
1199  @member Date getElapsed
1200  */
1201 Date.prototype.getElapsed = function(date) {
1202         return Math.abs((date || new Date()).getTime()-this.getTime());
1203 };
1204 // was in date file..
1205
1206
1207 // private
1208 Date.parseFunctions = {count:0};
1209 // private
1210 Date.parseRegexes = [];
1211 // private
1212 Date.formatFunctions = {count:0};
1213
1214 // private
1215 Date.prototype.dateFormat = function(format) {
1216     if (Date.formatFunctions[format] == null) {
1217         Date.createNewFormat(format);
1218     }
1219     var func = Date.formatFunctions[format];
1220     return this[func]();
1221 };
1222
1223
1224 /**
1225  * Formats a date given the supplied format string
1226  * @param {String} format The format string
1227  * @return {String} The formatted date
1228  * @method
1229  */
1230 Date.prototype.format = Date.prototype.dateFormat;
1231
1232 // private
1233 Date.createNewFormat = function(format) {
1234     var funcName = "format" + Date.formatFunctions.count++;
1235     Date.formatFunctions[format] = funcName;
1236     var code = "Date.prototype." + funcName + " = function(){return ";
1237     var special = false;
1238     var ch = '';
1239     for (var i = 0; i < format.length; ++i) {
1240         ch = format.charAt(i);
1241         if (!special && ch == "\\") {
1242             special = true;
1243         }
1244         else if (special) {
1245             special = false;
1246             code += "'" + String.escape(ch) + "' + ";
1247         }
1248         else {
1249             code += Date.getFormatCode(ch);
1250         }
1251     }
1252     /** eval:var:zzzzzzzzzzzzz */
1253     eval(code.substring(0, code.length - 3) + ";}");
1254 };
1255
1256 // private
1257 Date.getFormatCode = function(character) {
1258     switch (character) {
1259     case "d":
1260         return "String.leftPad(this.getDate(), 2, '0') + ";
1261     case "D":
1262         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1263     case "j":
1264         return "this.getDate() + ";
1265     case "l":
1266         return "Date.dayNames[this.getDay()] + ";
1267     case "S":
1268         return "this.getSuffix() + ";
1269     case "w":
1270         return "this.getDay() + ";
1271     case "z":
1272         return "this.getDayOfYear() + ";
1273     case "W":
1274         return "this.getWeekOfYear() + ";
1275     case "F":
1276         return "Date.monthNames[this.getMonth()] + ";
1277     case "m":
1278         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1279     case "M":
1280         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1281     case "n":
1282         return "(this.getMonth() + 1) + ";
1283     case "t":
1284         return "this.getDaysInMonth() + ";
1285     case "L":
1286         return "(this.isLeapYear() ? 1 : 0) + ";
1287     case "Y":
1288         return "this.getFullYear() + ";
1289     case "y":
1290         return "('' + this.getFullYear()).substring(2, 4) + ";
1291     case "a":
1292         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1293     case "A":
1294         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1295     case "g":
1296         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1297     case "G":
1298         return "this.getHours() + ";
1299     case "h":
1300         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1301     case "H":
1302         return "String.leftPad(this.getHours(), 2, '0') + ";
1303     case "i":
1304         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1305     case "s":
1306         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1307     case "O":
1308         return "this.getGMTOffset() + ";
1309     case "P":
1310         return "this.getGMTColonOffset() + ";
1311     case "T":
1312         return "this.getTimezone() + ";
1313     case "Z":
1314         return "(this.getTimezoneOffset() * -60) + ";
1315     default:
1316         return "'" + String.escape(character) + "' + ";
1317     }
1318 };
1319
1320 /**
1321  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1322  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1323  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1324  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1325  * string or the parse operation will fail.
1326  * Example Usage:
1327 <pre><code>
1328 //dt = Fri May 25 2007 (current date)
1329 var dt = new Date();
1330
1331 //dt = Thu May 25 2006 (today's month/day in 2006)
1332 dt = Date.parseDate("2006", "Y");
1333
1334 //dt = Sun Jan 15 2006 (all date parts specified)
1335 dt = Date.parseDate("2006-1-15", "Y-m-d");
1336
1337 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1338 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1339 </code></pre>
1340  * @param {String} input The unparsed date as a string
1341  * @param {String} format The format the date is in
1342  * @return {Date} The parsed date
1343  * @static
1344  */
1345 Date.parseDate = function(input, format) {
1346     if (Date.parseFunctions[format] == null) {
1347         Date.createParser(format);
1348     }
1349     var func = Date.parseFunctions[format];
1350     return Date[func](input);
1351 };
1352 /**
1353  * @private
1354  */
1355
1356 Date.createParser = function(format) {
1357     var funcName = "parse" + Date.parseFunctions.count++;
1358     var regexNum = Date.parseRegexes.length;
1359     var currentGroup = 1;
1360     Date.parseFunctions[format] = funcName;
1361
1362     var code = "Date." + funcName + " = function(input){\n"
1363         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1364         + "var d = new Date();\n"
1365         + "y = d.getFullYear();\n"
1366         + "m = d.getMonth();\n"
1367         + "d = d.getDate();\n"
1368         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1369         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1370         + "if (results && results.length > 0) {";
1371     var regex = "";
1372
1373     var special = false;
1374     var ch = '';
1375     for (var i = 0; i < format.length; ++i) {
1376         ch = format.charAt(i);
1377         if (!special && ch == "\\") {
1378             special = true;
1379         }
1380         else if (special) {
1381             special = false;
1382             regex += String.escape(ch);
1383         }
1384         else {
1385             var obj = Date.formatCodeToRegex(ch, currentGroup);
1386             currentGroup += obj.g;
1387             regex += obj.s;
1388             if (obj.g && obj.c) {
1389                 code += obj.c;
1390             }
1391         }
1392     }
1393
1394     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1395         + "{v = new Date(y, m, d, h, i, s); v.setFullYear(y);}\n"
1396         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1397         + "{v = new Date(y, m, d, h, i); v.setFullYear(y);}\n"
1398         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1399         + "{v = new Date(y, m, d, h); v.setFullYear(y);}\n"
1400         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1401         + "{v = new Date(y, m, d); v.setFullYear(y);}\n"
1402         + "else if (y >= 0 && m >= 0)\n"
1403         + "{v = new Date(y, m); v.setFullYear(y);}\n"
1404         + "else if (y >= 0)\n"
1405         + "{v = new Date(y); v.setFullYear(y);}\n"
1406         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1407         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1408         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1409         + ";}";
1410
1411     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1412     /** eval:var:zzzzzzzzzzzzz */
1413     eval(code);
1414 };
1415
1416 // private
1417 Date.formatCodeToRegex = function(character, currentGroup) {
1418     switch (character) {
1419     case "D":
1420         return {g:0,
1421         c:null,
1422         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1423     case "j":
1424         return {g:1,
1425             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1426             s:"(\\d{1,2})"}; // day of month without leading zeroes
1427     case "d":
1428         return {g:1,
1429             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1430             s:"(\\d{2})"}; // day of month with leading zeroes
1431     case "l":
1432         return {g:0,
1433             c:null,
1434             s:"(?:" + Date.dayNames.join("|") + ")"};
1435     case "S":
1436         return {g:0,
1437             c:null,
1438             s:"(?:st|nd|rd|th)"};
1439     case "w":
1440         return {g:0,
1441             c:null,
1442             s:"\\d"};
1443     case "z":
1444         return {g:0,
1445             c:null,
1446             s:"(?:\\d{1,3})"};
1447     case "W":
1448         return {g:0,
1449             c:null,
1450             s:"(?:\\d{2})"};
1451     case "F":
1452         return {g:1,
1453             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1454             s:"(" + Date.monthNames.join("|") + ")"};
1455     case "M":
1456         return {g:1,
1457             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1458             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1459     case "n":
1460         return {g:1,
1461             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1462             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1463     case "m":
1464         return {g:1,
1465             c:"m = Math.max(0,parseInt(results[" + currentGroup + "], 10) - 1);\n",
1466             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1467     case "t":
1468         return {g:0,
1469             c:null,
1470             s:"\\d{1,2}"};
1471     case "L":
1472         return {g:0,
1473             c:null,
1474             s:"(?:1|0)"};
1475     case "Y":
1476         return {g:1,
1477             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1478             s:"(\\d{4})"};
1479     case "y":
1480         return {g:1,
1481             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1482                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1483             s:"(\\d{1,2})"};
1484     case "a":
1485         return {g:1,
1486             c:"if (results[" + currentGroup + "] == 'am') {\n"
1487                 + "if (h == 12) { h = 0; }\n"
1488                 + "} else { if (h < 12) { h += 12; }}",
1489             s:"(am|pm)"};
1490     case "A":
1491         return {g:1,
1492             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1493                 + "if (h == 12) { h = 0; }\n"
1494                 + "} else { if (h < 12) { h += 12; }}",
1495             s:"(AM|PM)"};
1496     case "g":
1497     case "G":
1498         return {g:1,
1499             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1500             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1501     case "h":
1502     case "H":
1503         return {g:1,
1504             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1505             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1506     case "i":
1507         return {g:1,
1508             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1509             s:"(\\d{2})"};
1510     case "s":
1511         return {g:1,
1512             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1513             s:"(\\d{2})"};
1514     case "O":
1515         return {g:1,
1516             c:[
1517                 "o = results[", currentGroup, "];\n",
1518                 "var sn = o.substring(0,1);\n", // get + / - sign
1519                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1520                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1521                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1522                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1523             ].join(""),
1524             s:"([+\-]\\d{2,4})"};
1525     
1526     
1527     case "P":
1528         return {g:1,
1529                 c:[
1530                    "o = results[", currentGroup, "];\n",
1531                    "var sn = o.substring(0,1);\n",
1532                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1533                    "var mn = o.substring(4,6) % 60;\n",
1534                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1535                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1536             ].join(""),
1537             s:"([+\-]\\d{4})"};
1538     case "T":
1539         return {g:0,
1540             c:null,
1541             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1542     case "Z":
1543         return {g:1,
1544             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1545                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1546             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1547     default:
1548         return {g:0,
1549             c:null,
1550             s:String.escape(character)};
1551     }
1552 };
1553
1554 /**
1555  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1556  * @return {String} The abbreviated timezone name (e.g. 'CST')
1557  */
1558 Date.prototype.getTimezone = function() {
1559     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1560 };
1561
1562 /**
1563  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1564  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1565  */
1566 Date.prototype.getGMTOffset = function() {
1567     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1568         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1569         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1570 };
1571
1572 /**
1573  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1574  * @return {String} 2-characters representing hours and 2-characters representing minutes
1575  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1576  */
1577 Date.prototype.getGMTColonOffset = function() {
1578         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1579                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1580                 + ":"
1581                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1582 }
1583
1584 /**
1585  * Get the numeric day number of the year, adjusted for leap year.
1586  * @return {Number} 0 through 364 (365 in leap years)
1587  */
1588 Date.prototype.getDayOfYear = function() {
1589     var num = 0;
1590     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1591     for (var i = 0; i < this.getMonth(); ++i) {
1592         num += Date.daysInMonth[i];
1593     }
1594     return num + this.getDate() - 1;
1595 };
1596
1597 /**
1598  * Get the string representation of the numeric week number of the year
1599  * (equivalent to the format specifier 'W').
1600  * @return {String} '00' through '52'
1601  */
1602 Date.prototype.getWeekOfYear = function() {
1603     // Skip to Thursday of this week
1604     var now = this.getDayOfYear() + (4 - this.getDay());
1605     // Find the first Thursday of the year
1606     var jan1 = new Date(this.getFullYear(), 0, 1);
1607     var then = (7 - jan1.getDay() + 4);
1608     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1609 };
1610
1611 /**
1612  * Whether or not the current date is in a leap year.
1613  * @return {Boolean} True if the current date is in a leap year, else false
1614  */
1615 Date.prototype.isLeapYear = function() {
1616     var year = this.getFullYear();
1617     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1618 };
1619
1620 /**
1621  * Get the first day of the current month, adjusted for leap year.  The returned value
1622  * is the numeric day index within the week (0-6) which can be used in conjunction with
1623  * the {@link #monthNames} array to retrieve the textual day name.
1624  * Example:
1625  *<pre><code>
1626 var dt = new Date('1/10/2007');
1627 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1628 </code></pre>
1629  * @return {Number} The day number (0-6)
1630  */
1631 Date.prototype.getFirstDayOfMonth = function() {
1632     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1633     return (day < 0) ? (day + 7) : day;
1634 };
1635
1636 /**
1637  * Get the last day of the current month, adjusted for leap year.  The returned value
1638  * is the numeric day index within the week (0-6) which can be used in conjunction with
1639  * the {@link #monthNames} array to retrieve the textual day name.
1640  * Example:
1641  *<pre><code>
1642 var dt = new Date('1/10/2007');
1643 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1644 </code></pre>
1645  * @return {Number} The day number (0-6)
1646  */
1647 Date.prototype.getLastDayOfMonth = function() {
1648     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1649     return (day < 0) ? (day + 7) : day;
1650 };
1651
1652
1653 /**
1654  * Get the first date of this date's month
1655  * @return {Date}
1656  */
1657 Date.prototype.getFirstDateOfMonth = function() {
1658     return new Date(this.getFullYear(), this.getMonth(), 1);
1659 };
1660
1661 /**
1662  * Get the last date of this date's month
1663  * @return {Date}
1664  */
1665 Date.prototype.getLastDateOfMonth = function() {
1666     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1667 };
1668 /**
1669  * Get the number of days in the current month, adjusted for leap year.
1670  * @return {Number} The number of days in the month
1671  */
1672 Date.prototype.getDaysInMonth = function() {
1673     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1674     return Date.daysInMonth[this.getMonth()];
1675 };
1676
1677 /**
1678  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1679  * @return {String} 'st, 'nd', 'rd' or 'th'
1680  */
1681 Date.prototype.getSuffix = function() {
1682     switch (this.getDate()) {
1683         case 1:
1684         case 21:
1685         case 31:
1686             return "st";
1687         case 2:
1688         case 22:
1689             return "nd";
1690         case 3:
1691         case 23:
1692             return "rd";
1693         default:
1694             return "th";
1695     }
1696 };
1697
1698 // private
1699 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1700
1701 /**
1702  * An array of textual month names.
1703  * Override these values for international dates, for example...
1704  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1705  * @type Array
1706  * @static
1707  */
1708 Date.monthNames =
1709    ["January",
1710     "February",
1711     "March",
1712     "April",
1713     "May",
1714     "June",
1715     "July",
1716     "August",
1717     "September",
1718     "October",
1719     "November",
1720     "December"];
1721
1722 /**
1723  * An array of textual day names.
1724  * Override these values for international dates, for example...
1725  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1726  * @type Array
1727  * @static
1728  */
1729 Date.dayNames =
1730    ["Sunday",
1731     "Monday",
1732     "Tuesday",
1733     "Wednesday",
1734     "Thursday",
1735     "Friday",
1736     "Saturday"];
1737
1738 // private
1739 Date.y2kYear = 50;
1740 // private
1741 Date.monthNumbers = {
1742     Jan:0,
1743     Feb:1,
1744     Mar:2,
1745     Apr:3,
1746     May:4,
1747     Jun:5,
1748     Jul:6,
1749     Aug:7,
1750     Sep:8,
1751     Oct:9,
1752     Nov:10,
1753     Dec:11};
1754
1755 /**
1756  * Creates and returns a new Date instance with the exact same date value as the called instance.
1757  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1758  * variable will also be changed.  When the intention is to create a new variable that will not
1759  * modify the original instance, you should create a clone.
1760  *
1761  * Example of correctly cloning a date:
1762  * <pre><code>
1763 //wrong way:
1764 var orig = new Date('10/1/2006');
1765 var copy = orig;
1766 copy.setDate(5);
1767 document.write(orig);  //returns 'Thu Oct 05 2006'!
1768
1769 //correct way:
1770 var orig = new Date('10/1/2006');
1771 var copy = orig.clone();
1772 copy.setDate(5);
1773 document.write(orig);  //returns 'Thu Oct 01 2006'
1774 </code></pre>
1775  * @return {Date} The new Date instance
1776  */
1777 Date.prototype.clone = function() {
1778         return new Date(this.getTime());
1779 };
1780
1781 /**
1782  * Clears any time information from this date
1783  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1784  @return {Date} this or the clone
1785  */
1786 Date.prototype.clearTime = function(clone){
1787     if(clone){
1788         return this.clone().clearTime();
1789     }
1790     this.setHours(0);
1791     this.setMinutes(0);
1792     this.setSeconds(0);
1793     this.setMilliseconds(0);
1794     return this;
1795 };
1796
1797 // private
1798 // safari setMonth is broken -- check that this is only donw once...
1799 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1800     Date.brokenSetMonth = Date.prototype.setMonth;
1801         Date.prototype.setMonth = function(num){
1802                 if(num <= -1){
1803                         var n = Math.ceil(-num);
1804                         var back_year = Math.ceil(n/12);
1805                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1806                         this.setFullYear(this.getFullYear() - back_year);
1807                         return Date.brokenSetMonth.call(this, month);
1808                 } else {
1809                         return Date.brokenSetMonth.apply(this, arguments);
1810                 }
1811         };
1812 }
1813
1814 /** Date interval constant 
1815 * @static 
1816 * @type String */
1817 Date.MILLI = "ms";
1818 /** Date interval constant 
1819 * @static 
1820 * @type String */
1821 Date.SECOND = "s";
1822 /** Date interval constant 
1823 * @static 
1824 * @type String */
1825 Date.MINUTE = "mi";
1826 /** Date interval constant 
1827 * @static 
1828 * @type String */
1829 Date.HOUR = "h";
1830 /** Date interval constant 
1831 * @static 
1832 * @type String */
1833 Date.DAY = "d";
1834 /** Date interval constant 
1835 * @static 
1836 * @type String */
1837 Date.MONTH = "mo";
1838 /** Date interval constant 
1839 * @static 
1840 * @type String */
1841 Date.YEAR = "y";
1842
1843 /**
1844  * Provides a convenient method of performing basic date arithmetic.  This method
1845  * does not modify the Date instance being called - it creates and returns
1846  * a new Date instance containing the resulting date value.
1847  *
1848  * Examples:
1849  * <pre><code>
1850 //Basic usage:
1851 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1852 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1853
1854 //Negative values will subtract correctly:
1855 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1856 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1857
1858 //You can even chain several calls together in one line!
1859 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1860 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1861  </code></pre>
1862  *
1863  * @param {String} interval   A valid date interval enum value
1864  * @param {Number} value      The amount to add to the current date
1865  * @return {Date} The new Date instance
1866  */
1867 Date.prototype.add = function(interval, value){
1868   var d = this.clone();
1869   if (!interval || value === 0) { return d; }
1870   switch(interval.toLowerCase()){
1871     case Date.MILLI:
1872       d.setMilliseconds(this.getMilliseconds() + value);
1873       break;
1874     case Date.SECOND:
1875       d.setSeconds(this.getSeconds() + value);
1876       break;
1877     case Date.MINUTE:
1878       d.setMinutes(this.getMinutes() + value);
1879       break;
1880     case Date.HOUR:
1881       d.setHours(this.getHours() + value);
1882       break;
1883     case Date.DAY:
1884       d.setDate(this.getDate() + value);
1885       break;
1886     case Date.MONTH:
1887       var day = this.getDate();
1888       if(day > 28){
1889           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1890       }
1891       d.setDate(day);
1892       d.setMonth(this.getMonth() + value);
1893       break;
1894     case Date.YEAR:
1895       d.setFullYear(this.getFullYear() + value);
1896       break;
1897   }
1898   return d;
1899 };
1900 /**
1901  * @class Roo.lib.Dom
1902  * @licence LGPL
1903  * @static
1904  * 
1905  * Dom utils (from YIU afaik)
1906  *
1907  * 
1908  **/
1909 Roo.lib.Dom = {
1910     /**
1911      * Get the view width
1912      * @param {Boolean} full True will get the full document, otherwise it's the view width
1913      * @return {Number} The width
1914      */
1915      
1916     getViewWidth : function(full) {
1917         return full ? this.getDocumentWidth() : this.getViewportWidth();
1918     },
1919     /**
1920      * Get the view height
1921      * @param {Boolean} full True will get the full document, otherwise it's the view height
1922      * @return {Number} The height
1923      */
1924     getViewHeight : function(full) {
1925         return full ? this.getDocumentHeight() : this.getViewportHeight();
1926     },
1927     /**
1928      * Get the Full Document height 
1929      * @return {Number} The height
1930      */
1931     getDocumentHeight: function() {
1932         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1933         return Math.max(scrollHeight, this.getViewportHeight());
1934     },
1935     /**
1936      * Get the Full Document width
1937      * @return {Number} The width
1938      */
1939     getDocumentWidth: function() {
1940         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1941         return Math.max(scrollWidth, this.getViewportWidth());
1942     },
1943     /**
1944      * Get the Window Viewport height
1945      * @return {Number} The height
1946      */
1947     getViewportHeight: function() {
1948         var height = self.innerHeight;
1949         var mode = document.compatMode;
1950
1951         if ((mode || Roo.isIE) && !Roo.isOpera) {
1952             height = (mode == "CSS1Compat") ?
1953                      document.documentElement.clientHeight :
1954                      document.body.clientHeight;
1955         }
1956
1957         return height;
1958     },
1959     /**
1960      * Get the Window Viewport width
1961      * @return {Number} The width
1962      */
1963     getViewportWidth: function() {
1964         var width = self.innerWidth;
1965         var mode = document.compatMode;
1966
1967         if (mode || Roo.isIE) {
1968             width = (mode == "CSS1Compat") ?
1969                     document.documentElement.clientWidth :
1970                     document.body.clientWidth;
1971         }
1972         return width;
1973     },
1974
1975     isAncestor : function(p, c) {
1976         p = Roo.getDom(p);
1977         c = Roo.getDom(c);
1978         if (!p || !c) {
1979             return false;
1980         }
1981
1982         if (p.contains && !Roo.isSafari) {
1983             return p.contains(c);
1984         } else if (p.compareDocumentPosition) {
1985             return !!(p.compareDocumentPosition(c) & 16);
1986         } else {
1987             var parent = c.parentNode;
1988             while (parent) {
1989                 if (parent == p) {
1990                     return true;
1991                 }
1992                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1993                     return false;
1994                 }
1995                 parent = parent.parentNode;
1996             }
1997             return false;
1998         }
1999     },
2000
2001     getRegion : function(el) {
2002         return Roo.lib.Region.getRegion(el);
2003     },
2004
2005     getY : function(el) {
2006         return this.getXY(el)[1];
2007     },
2008
2009     getX : function(el) {
2010         return this.getXY(el)[0];
2011     },
2012
2013     getXY : function(el) {
2014         var p, pe, b, scroll, bd = document.body;
2015         el = Roo.getDom(el);
2016         var fly = Roo.lib.AnimBase.fly;
2017         if (el.getBoundingClientRect) {
2018             b = el.getBoundingClientRect();
2019             scroll = fly(document).getScroll();
2020             return [b.left + scroll.left, b.top + scroll.top];
2021         }
2022         var x = 0, y = 0;
2023
2024         p = el;
2025
2026         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2027
2028         while (p) {
2029
2030             x += p.offsetLeft;
2031             y += p.offsetTop;
2032
2033             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2034                 hasAbsolute = true;
2035             }
2036
2037             if (Roo.isGecko) {
2038                 pe = fly(p);
2039
2040                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2041                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2042
2043
2044                 x += bl;
2045                 y += bt;
2046
2047
2048                 if (p != el && pe.getStyle('overflow') != 'visible') {
2049                     x += bl;
2050                     y += bt;
2051                 }
2052             }
2053             p = p.offsetParent;
2054         }
2055
2056         if (Roo.isSafari && hasAbsolute) {
2057             x -= bd.offsetLeft;
2058             y -= bd.offsetTop;
2059         }
2060
2061         if (Roo.isGecko && !hasAbsolute) {
2062             var dbd = fly(bd);
2063             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2064             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2065         }
2066
2067         p = el.parentNode;
2068         while (p && p != bd) {
2069             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2070                 x -= p.scrollLeft;
2071                 y -= p.scrollTop;
2072             }
2073             p = p.parentNode;
2074         }
2075         return [x, y];
2076     },
2077  
2078   
2079
2080
2081     setXY : function(el, xy) {
2082         el = Roo.fly(el, '_setXY');
2083         el.position();
2084         var pts = el.translatePoints(xy);
2085         if (xy[0] !== false) {
2086             el.dom.style.left = pts.left + "px";
2087         }
2088         if (xy[1] !== false) {
2089             el.dom.style.top = pts.top + "px";
2090         }
2091     },
2092
2093     setX : function(el, x) {
2094         this.setXY(el, [x, false]);
2095     },
2096
2097     setY : function(el, y) {
2098         this.setXY(el, [false, y]);
2099     }
2100 };
2101 /*
2102  * Portions of this file are based on pieces of Yahoo User Interface Library
2103  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2104  * YUI licensed under the BSD License:
2105  * http://developer.yahoo.net/yui/license.txt
2106  * <script type="text/javascript">
2107  *
2108  */
2109
2110 Roo.lib.Event = function() {
2111     var loadComplete = false;
2112     var listeners = [];
2113     var unloadListeners = [];
2114     var retryCount = 0;
2115     var onAvailStack = [];
2116     var counter = 0;
2117     var lastError = null;
2118
2119     return {
2120         POLL_RETRYS: 200,
2121         POLL_INTERVAL: 20,
2122         EL: 0,
2123         TYPE: 1,
2124         FN: 2,
2125         WFN: 3,
2126         OBJ: 3,
2127         ADJ_SCOPE: 4,
2128         _interval: null,
2129
2130         startInterval: function() {
2131             if (!this._interval) {
2132                 var self = this;
2133                 var callback = function() {
2134                     self._tryPreloadAttach();
2135                 };
2136                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2137
2138             }
2139         },
2140
2141         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2142             onAvailStack.push({ id:         p_id,
2143                 fn:         p_fn,
2144                 obj:        p_obj,
2145                 override:   p_override,
2146                 checkReady: false    });
2147
2148             retryCount = this.POLL_RETRYS;
2149             this.startInterval();
2150         },
2151
2152
2153         addListener: function(el, eventName, fn) {
2154             el = Roo.getDom(el);
2155             if (!el || !fn) {
2156                 return false;
2157             }
2158
2159             if ("unload" == eventName) {
2160                 unloadListeners[unloadListeners.length] =
2161                 [el, eventName, fn];
2162                 return true;
2163             }
2164
2165             var wrappedFn = function(e) {
2166                 return fn(Roo.lib.Event.getEvent(e));
2167             };
2168
2169             var li = [el, eventName, fn, wrappedFn];
2170
2171             var index = listeners.length;
2172             listeners[index] = li;
2173
2174             this.doAdd(el, eventName, wrappedFn, false);
2175             return true;
2176
2177         },
2178
2179
2180         removeListener: function(el, eventName, fn) {
2181             var i, len;
2182
2183             el = Roo.getDom(el);
2184
2185             if(!fn) {
2186                 return this.purgeElement(el, false, eventName);
2187             }
2188
2189
2190             if ("unload" == eventName) {
2191
2192                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2193                     var li = unloadListeners[i];
2194                     if (li &&
2195                         li[0] == el &&
2196                         li[1] == eventName &&
2197                         li[2] == fn) {
2198                         unloadListeners.splice(i, 1);
2199                         return true;
2200                     }
2201                 }
2202
2203                 return false;
2204             }
2205
2206             var cacheItem = null;
2207
2208
2209             var index = arguments[3];
2210
2211             if ("undefined" == typeof index) {
2212                 index = this._getCacheIndex(el, eventName, fn);
2213             }
2214
2215             if (index >= 0) {
2216                 cacheItem = listeners[index];
2217             }
2218
2219             if (!el || !cacheItem) {
2220                 return false;
2221             }
2222
2223             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2224
2225             delete listeners[index][this.WFN];
2226             delete listeners[index][this.FN];
2227             listeners.splice(index, 1);
2228
2229             return true;
2230
2231         },
2232
2233
2234         getTarget: function(ev, resolveTextNode) {
2235             ev = ev.browserEvent || ev;
2236             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2237             var t = ev.target || ev.srcElement;
2238             return this.resolveTextNode(t);
2239         },
2240
2241
2242         resolveTextNode: function(node) {
2243             if (Roo.isSafari && node && 3 == node.nodeType) {
2244                 return node.parentNode;
2245             } else {
2246                 return node;
2247             }
2248         },
2249
2250
2251         getPageX: function(ev) {
2252             ev = ev.browserEvent || ev;
2253             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2254             var x = ev.pageX;
2255             if (!x && 0 !== x) {
2256                 x = ev.clientX || 0;
2257
2258                 if (Roo.isIE) {
2259                     x += this.getScroll()[1];
2260                 }
2261             }
2262
2263             return x;
2264         },
2265
2266
2267         getPageY: function(ev) {
2268             ev = ev.browserEvent || ev;
2269             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2270             var y = ev.pageY;
2271             if (!y && 0 !== y) {
2272                 y = ev.clientY || 0;
2273
2274                 if (Roo.isIE) {
2275                     y += this.getScroll()[0];
2276                 }
2277             }
2278
2279
2280             return y;
2281         },
2282
2283
2284         getXY: function(ev) {
2285             ev = ev.browserEvent || ev;
2286             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2287             return [this.getPageX(ev), this.getPageY(ev)];
2288         },
2289
2290
2291         getRelatedTarget: function(ev) {
2292             ev = ev.browserEvent || ev;
2293             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2294             var t = ev.relatedTarget;
2295             if (!t) {
2296                 if (ev.type == "mouseout") {
2297                     t = ev.toElement;
2298                 } else if (ev.type == "mouseover") {
2299                     t = ev.fromElement;
2300                 }
2301             }
2302
2303             return this.resolveTextNode(t);
2304         },
2305
2306
2307         getTime: function(ev) {
2308             ev = ev.browserEvent || ev;
2309             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2310             if (!ev.time) {
2311                 var t = new Date().getTime();
2312                 try {
2313                     ev.time = t;
2314                 } catch(ex) {
2315                     this.lastError = ex;
2316                     return t;
2317                 }
2318             }
2319
2320             return ev.time;
2321         },
2322
2323
2324         stopEvent: function(ev) {
2325             this.stopPropagation(ev);
2326             this.preventDefault(ev);
2327         },
2328
2329
2330         stopPropagation: function(ev) {
2331             ev = ev.browserEvent || ev;
2332             if (ev.stopPropagation) {
2333                 ev.stopPropagation();
2334             } else {
2335                 ev.cancelBubble = true;
2336             }
2337         },
2338
2339
2340         preventDefault: function(ev) {
2341             ev = ev.browserEvent || ev;
2342             if(ev.preventDefault) {
2343                 ev.preventDefault();
2344             } else {
2345                 ev.returnValue = false;
2346             }
2347         },
2348
2349
2350         getEvent: function(e) {
2351             var ev = e || window.event;
2352             if (!ev) {
2353                 var c = this.getEvent.caller;
2354                 while (c) {
2355                     ev = c.arguments[0];
2356                     if (ev && Event == ev.constructor) {
2357                         break;
2358                     }
2359                     c = c.caller;
2360                 }
2361             }
2362             return ev;
2363         },
2364
2365
2366         getCharCode: function(ev) {
2367             ev = ev.browserEvent || ev;
2368             return ev.charCode || ev.keyCode || 0;
2369         },
2370
2371
2372         _getCacheIndex: function(el, eventName, fn) {
2373             for (var i = 0,len = listeners.length; i < len; ++i) {
2374                 var li = listeners[i];
2375                 if (li &&
2376                     li[this.FN] == fn &&
2377                     li[this.EL] == el &&
2378                     li[this.TYPE] == eventName) {
2379                     return i;
2380                 }
2381             }
2382
2383             return -1;
2384         },
2385
2386
2387         elCache: {},
2388
2389
2390         getEl: function(id) {
2391             return document.getElementById(id);
2392         },
2393
2394
2395         clearCache: function() {
2396         },
2397
2398
2399         _load: function(e) {
2400             loadComplete = true;
2401             var EU = Roo.lib.Event;
2402
2403
2404             if (Roo.isIE) {
2405                 EU.doRemove(window, "load", EU._load);
2406             }
2407         },
2408
2409
2410         _tryPreloadAttach: function() {
2411
2412             if (this.locked) {
2413                 return false;
2414             }
2415
2416             this.locked = true;
2417
2418
2419             var tryAgain = !loadComplete;
2420             if (!tryAgain) {
2421                 tryAgain = (retryCount > 0);
2422             }
2423
2424
2425             var notAvail = [];
2426             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2427                 var item = onAvailStack[i];
2428                 if (item) {
2429                     var el = this.getEl(item.id);
2430
2431                     if (el) {
2432                         if (!item.checkReady ||
2433                             loadComplete ||
2434                             el.nextSibling ||
2435                             (document && document.body)) {
2436
2437                             var scope = el;
2438                             if (item.override) {
2439                                 if (item.override === true) {
2440                                     scope = item.obj;
2441                                 } else {
2442                                     scope = item.override;
2443                                 }
2444                             }
2445                             item.fn.call(scope, item.obj);
2446                             onAvailStack[i] = null;
2447                         }
2448                     } else {
2449                         notAvail.push(item);
2450                     }
2451                 }
2452             }
2453
2454             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2455
2456             if (tryAgain) {
2457
2458                 this.startInterval();
2459             } else {
2460                 clearInterval(this._interval);
2461                 this._interval = null;
2462             }
2463
2464             this.locked = false;
2465
2466             return true;
2467
2468         },
2469
2470
2471         purgeElement: function(el, recurse, eventName) {
2472             var elListeners = this.getListeners(el, eventName);
2473             if (elListeners) {
2474                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2475                     var l = elListeners[i];
2476                     this.removeListener(el, l.type, l.fn);
2477                 }
2478             }
2479
2480             if (recurse && el && el.childNodes) {
2481                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2482                     this.purgeElement(el.childNodes[i], recurse, eventName);
2483                 }
2484             }
2485         },
2486
2487
2488         getListeners: function(el, eventName) {
2489             var results = [], searchLists;
2490             if (!eventName) {
2491                 searchLists = [listeners, unloadListeners];
2492             } else if (eventName == "unload") {
2493                 searchLists = [unloadListeners];
2494             } else {
2495                 searchLists = [listeners];
2496             }
2497
2498             for (var j = 0; j < searchLists.length; ++j) {
2499                 var searchList = searchLists[j];
2500                 if (searchList && searchList.length > 0) {
2501                     for (var i = 0,len = searchList.length; i < len; ++i) {
2502                         var l = searchList[i];
2503                         if (l && l[this.EL] === el &&
2504                             (!eventName || eventName === l[this.TYPE])) {
2505                             results.push({
2506                                 type:   l[this.TYPE],
2507                                 fn:     l[this.FN],
2508                                 obj:    l[this.OBJ],
2509                                 adjust: l[this.ADJ_SCOPE],
2510                                 index:  i
2511                             });
2512                         }
2513                     }
2514                 }
2515             }
2516
2517             return (results.length) ? results : null;
2518         },
2519
2520
2521         _unload: function(e) {
2522
2523             var EU = Roo.lib.Event, i, j, l, len, index;
2524
2525             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2526                 l = unloadListeners[i];
2527                 if (l) {
2528                     var scope = window;
2529                     if (l[EU.ADJ_SCOPE]) {
2530                         if (l[EU.ADJ_SCOPE] === true) {
2531                             scope = l[EU.OBJ];
2532                         } else {
2533                             scope = l[EU.ADJ_SCOPE];
2534                         }
2535                     }
2536                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2537                     unloadListeners[i] = null;
2538                     l = null;
2539                     scope = null;
2540                 }
2541             }
2542
2543             unloadListeners = null;
2544
2545             if (listeners && listeners.length > 0) {
2546                 j = listeners.length;
2547                 while (j) {
2548                     index = j - 1;
2549                     l = listeners[index];
2550                     if (l) {
2551                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2552                                 l[EU.FN], index);
2553                     }
2554                     j = j - 1;
2555                 }
2556                 l = null;
2557
2558                 EU.clearCache();
2559             }
2560
2561             EU.doRemove(window, "unload", EU._unload);
2562
2563         },
2564
2565
2566         getScroll: function() {
2567             var dd = document.documentElement, db = document.body;
2568             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2569                 return [dd.scrollTop, dd.scrollLeft];
2570             } else if (db) {
2571                 return [db.scrollTop, db.scrollLeft];
2572             } else {
2573                 return [0, 0];
2574             }
2575         },
2576
2577
2578         doAdd: function () {
2579             if (window.addEventListener) {
2580                 return function(el, eventName, fn, capture) {
2581                     el.addEventListener(eventName, fn, (capture));
2582                 };
2583             } else if (window.attachEvent) {
2584                 return function(el, eventName, fn, capture) {
2585                     el.attachEvent("on" + eventName, fn);
2586                 };
2587             } else {
2588                 return function() {
2589                 };
2590             }
2591         }(),
2592
2593
2594         doRemove: function() {
2595             if (window.removeEventListener) {
2596                 return function (el, eventName, fn, capture) {
2597                     el.removeEventListener(eventName, fn, (capture));
2598                 };
2599             } else if (window.detachEvent) {
2600                 return function (el, eventName, fn) {
2601                     el.detachEvent("on" + eventName, fn);
2602                 };
2603             } else {
2604                 return function() {
2605                 };
2606             }
2607         }()
2608     };
2609     
2610 }();
2611 (function() {     
2612    
2613     var E = Roo.lib.Event;
2614     E.on = E.addListener;
2615     E.un = E.removeListener;
2616
2617     if (document && document.body) {
2618         E._load();
2619     } else {
2620         E.doAdd(window, "load", E._load);
2621     }
2622     E.doAdd(window, "unload", E._unload);
2623     E._tryPreloadAttach();
2624 })();
2625
2626  
2627
2628 (function() {
2629     /**
2630      * @class Roo.lib.Ajax
2631      *
2632      * provide a simple Ajax request utility functions
2633      * 
2634      * Portions of this file are based on pieces of Yahoo User Interface Library
2635     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2636     * YUI licensed under the BSD License:
2637     * http://developer.yahoo.net/yui/license.txt
2638     * <script type="text/javascript">
2639     *
2640      *
2641      */
2642     Roo.lib.Ajax = {
2643         /**
2644          * @static 
2645          */
2646         request : function(method, uri, cb, data, options) {
2647             if(options){
2648                 var hs = options.headers;
2649                 if(hs){
2650                     for(var h in hs){
2651                         if(hs.hasOwnProperty(h)){
2652                             this.initHeader(h, hs[h], false);
2653                         }
2654                     }
2655                 }
2656                 if(options.xmlData){
2657                     this.initHeader('Content-Type', 'text/xml', false);
2658                     method = 'POST';
2659                     data = options.xmlData;
2660                 }
2661             }
2662
2663             return this.asyncRequest(method, uri, cb, data);
2664         },
2665         /**
2666          * serialize a form
2667          *
2668          * @static
2669          * @param {DomForm} form element
2670          * @return {String} urlencode form output.
2671          */
2672         serializeForm : function(form) {
2673             if(typeof form == 'string') {
2674                 form = (document.getElementById(form) || document.forms[form]);
2675             }
2676
2677             var el, name, val, disabled, data = '', hasSubmit = false;
2678             for (var i = 0; i < form.elements.length; i++) {
2679                 el = form.elements[i];
2680                 disabled = form.elements[i].disabled;
2681                 name = form.elements[i].name;
2682                 val = form.elements[i].value;
2683
2684                 if (!disabled && name){
2685                     switch (el.type)
2686                             {
2687                         case 'select-one':
2688                         case 'select-multiple':
2689                             for (var j = 0; j < el.options.length; j++) {
2690                                 if (el.options[j].selected) {
2691                                     if (Roo.isIE) {
2692                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2693                                     }
2694                                     else {
2695                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2696                                     }
2697                                 }
2698                             }
2699                             break;
2700                         case 'radio':
2701                         case 'checkbox':
2702                             if (el.checked) {
2703                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2704                             }
2705                             break;
2706                         case 'file':
2707
2708                         case undefined:
2709
2710                         case 'reset':
2711
2712                         case 'button':
2713
2714                             break;
2715                         case 'submit':
2716                             if(hasSubmit == false) {
2717                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2718                                 hasSubmit = true;
2719                             }
2720                             break;
2721                         default:
2722                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2723                             break;
2724                     }
2725                 }
2726             }
2727             data = data.substr(0, data.length - 1);
2728             return data;
2729         },
2730
2731         headers:{},
2732
2733         hasHeaders:false,
2734
2735         useDefaultHeader:true,
2736
2737         defaultPostHeader:'application/x-www-form-urlencoded',
2738
2739         useDefaultXhrHeader:true,
2740
2741         defaultXhrHeader:'XMLHttpRequest',
2742
2743         hasDefaultHeaders:true,
2744
2745         defaultHeaders:{},
2746
2747         poll:{},
2748
2749         timeout:{},
2750
2751         pollInterval:50,
2752
2753         transactionId:0,
2754
2755         setProgId:function(id)
2756         {
2757             this.activeX.unshift(id);
2758         },
2759
2760         setDefaultPostHeader:function(b)
2761         {
2762             this.useDefaultHeader = b;
2763         },
2764
2765         setDefaultXhrHeader:function(b)
2766         {
2767             this.useDefaultXhrHeader = b;
2768         },
2769
2770         setPollingInterval:function(i)
2771         {
2772             if (typeof i == 'number' && isFinite(i)) {
2773                 this.pollInterval = i;
2774             }
2775         },
2776
2777         createXhrObject:function(transactionId)
2778         {
2779             var obj,http;
2780             try
2781             {
2782
2783                 http = new XMLHttpRequest();
2784
2785                 obj = { conn:http, tId:transactionId };
2786             }
2787             catch(e)
2788             {
2789                 for (var i = 0; i < this.activeX.length; ++i) {
2790                     try
2791                     {
2792
2793                         http = new ActiveXObject(this.activeX[i]);
2794
2795                         obj = { conn:http, tId:transactionId };
2796                         break;
2797                     }
2798                     catch(e) {
2799                     }
2800                 }
2801             }
2802             finally
2803             {
2804                 return obj;
2805             }
2806         },
2807
2808         getConnectionObject:function()
2809         {
2810             var o;
2811             var tId = this.transactionId;
2812
2813             try
2814             {
2815                 o = this.createXhrObject(tId);
2816                 if (o) {
2817                     this.transactionId++;
2818                 }
2819             }
2820             catch(e) {
2821             }
2822             finally
2823             {
2824                 return o;
2825             }
2826         },
2827
2828         asyncRequest:function(method, uri, callback, postData)
2829         {
2830             var o = this.getConnectionObject();
2831
2832             if (!o) {
2833                 return null;
2834             }
2835             else {
2836                 o.conn.open(method, uri, true);
2837
2838                 if (this.useDefaultXhrHeader) {
2839                     if (!this.defaultHeaders['X-Requested-With']) {
2840                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2841                     }
2842                 }
2843
2844                 if(postData && this.useDefaultHeader){
2845                     this.initHeader('Content-Type', this.defaultPostHeader);
2846                 }
2847
2848                  if (this.hasDefaultHeaders || this.hasHeaders) {
2849                     this.setHeader(o);
2850                 }
2851
2852                 this.handleReadyState(o, callback);
2853                 o.conn.send(postData || null);
2854
2855                 return o;
2856             }
2857         },
2858
2859         handleReadyState:function(o, callback)
2860         {
2861             var oConn = this;
2862
2863             if (callback && callback.timeout) {
2864                 
2865                 this.timeout[o.tId] = window.setTimeout(function() {
2866                     oConn.abort(o, callback, true);
2867                 }, callback.timeout);
2868             }
2869
2870             this.poll[o.tId] = window.setInterval(
2871                     function() {
2872                         if (o.conn && o.conn.readyState == 4) {
2873                             window.clearInterval(oConn.poll[o.tId]);
2874                             delete oConn.poll[o.tId];
2875
2876                             if(callback && callback.timeout) {
2877                                 window.clearTimeout(oConn.timeout[o.tId]);
2878                                 delete oConn.timeout[o.tId];
2879                             }
2880
2881                             oConn.handleTransactionResponse(o, callback);
2882                         }
2883                     }
2884                     , this.pollInterval);
2885         },
2886
2887         handleTransactionResponse:function(o, callback, isAbort)
2888         {
2889
2890             if (!callback) {
2891                 this.releaseObject(o);
2892                 return;
2893             }
2894
2895             var httpStatus, responseObject;
2896
2897             try
2898             {
2899                 if (o.conn.status !== undefined && o.conn.status != 0) {
2900                     httpStatus = o.conn.status;
2901                 }
2902                 else {
2903                     httpStatus = 13030;
2904                 }
2905             }
2906             catch(e) {
2907
2908
2909                 httpStatus = 13030;
2910             }
2911
2912             if (httpStatus >= 200 && httpStatus < 300) {
2913                 responseObject = this.createResponseObject(o, callback.argument);
2914                 if (callback.success) {
2915                     if (!callback.scope) {
2916                         callback.success(responseObject);
2917                     }
2918                     else {
2919
2920
2921                         callback.success.apply(callback.scope, [responseObject]);
2922                     }
2923                 }
2924             }
2925             else {
2926                 switch (httpStatus) {
2927
2928                     case 12002:
2929                     case 12029:
2930                     case 12030:
2931                     case 12031:
2932                     case 12152:
2933                     case 13030:
2934                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2935                         if (callback.failure) {
2936                             if (!callback.scope) {
2937                                 callback.failure(responseObject);
2938                             }
2939                             else {
2940                                 callback.failure.apply(callback.scope, [responseObject]);
2941                             }
2942                         }
2943                         break;
2944                     default:
2945                         responseObject = this.createResponseObject(o, callback.argument);
2946                         if (callback.failure) {
2947                             if (!callback.scope) {
2948                                 callback.failure(responseObject);
2949                             }
2950                             else {
2951                                 callback.failure.apply(callback.scope, [responseObject]);
2952                             }
2953                         }
2954                 }
2955             }
2956
2957             this.releaseObject(o);
2958             responseObject = null;
2959         },
2960
2961         createResponseObject:function(o, callbackArg)
2962         {
2963             var obj = {};
2964             var headerObj = {};
2965
2966             try
2967             {
2968                 var headerStr = o.conn.getAllResponseHeaders();
2969                 var header = headerStr.split('\n');
2970                 for (var i = 0; i < header.length; i++) {
2971                     var delimitPos = header[i].indexOf(':');
2972                     if (delimitPos != -1) {
2973                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2974                     }
2975                 }
2976             }
2977             catch(e) {
2978             }
2979
2980             obj.tId = o.tId;
2981             obj.status = o.conn.status;
2982             obj.statusText = o.conn.statusText;
2983             obj.getResponseHeader = headerObj;
2984             obj.getAllResponseHeaders = headerStr;
2985             obj.responseText = o.conn.responseText;
2986             obj.responseXML = o.conn.responseXML;
2987
2988             if (typeof callbackArg !== undefined) {
2989                 obj.argument = callbackArg;
2990             }
2991
2992             return obj;
2993         },
2994
2995         createExceptionObject:function(tId, callbackArg, isAbort)
2996         {
2997             var COMM_CODE = 0;
2998             var COMM_ERROR = 'communication failure';
2999             var ABORT_CODE = -1;
3000             var ABORT_ERROR = 'transaction aborted';
3001
3002             var obj = {};
3003
3004             obj.tId = tId;
3005             if (isAbort) {
3006                 obj.status = ABORT_CODE;
3007                 obj.statusText = ABORT_ERROR;
3008             }
3009             else {
3010                 obj.status = COMM_CODE;
3011                 obj.statusText = COMM_ERROR;
3012             }
3013
3014             if (callbackArg) {
3015                 obj.argument = callbackArg;
3016             }
3017
3018             return obj;
3019         },
3020
3021         initHeader:function(label, value, isDefault)
3022         {
3023             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3024
3025             if (headerObj[label] === undefined) {
3026                 headerObj[label] = value;
3027             }
3028             else {
3029
3030
3031                 headerObj[label] = value + "," + headerObj[label];
3032             }
3033
3034             if (isDefault) {
3035                 this.hasDefaultHeaders = true;
3036             }
3037             else {
3038                 this.hasHeaders = true;
3039             }
3040         },
3041
3042
3043         setHeader:function(o)
3044         {
3045             if (this.hasDefaultHeaders) {
3046                 for (var prop in this.defaultHeaders) {
3047                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3048                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3049                     }
3050                 }
3051             }
3052
3053             if (this.hasHeaders) {
3054                 for (var prop in this.headers) {
3055                     if (this.headers.hasOwnProperty(prop)) {
3056                         o.conn.setRequestHeader(prop, this.headers[prop]);
3057                     }
3058                 }
3059                 this.headers = {};
3060                 this.hasHeaders = false;
3061             }
3062         },
3063
3064         resetDefaultHeaders:function() {
3065             delete this.defaultHeaders;
3066             this.defaultHeaders = {};
3067             this.hasDefaultHeaders = false;
3068         },
3069
3070         abort:function(o, callback, isTimeout)
3071         {
3072             if(this.isCallInProgress(o)) {
3073                 o.conn.abort();
3074                 window.clearInterval(this.poll[o.tId]);
3075                 delete this.poll[o.tId];
3076                 if (isTimeout) {
3077                     delete this.timeout[o.tId];
3078                 }
3079
3080                 this.handleTransactionResponse(o, callback, true);
3081
3082                 return true;
3083             }
3084             else {
3085                 return false;
3086             }
3087         },
3088
3089
3090         isCallInProgress:function(o)
3091         {
3092             if (o && o.conn) {
3093                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3094             }
3095             else {
3096
3097                 return false;
3098             }
3099         },
3100
3101
3102         releaseObject:function(o)
3103         {
3104
3105             o.conn = null;
3106
3107             o = null;
3108         },
3109
3110         activeX:[
3111         'MSXML2.XMLHTTP.3.0',
3112         'MSXML2.XMLHTTP',
3113         'Microsoft.XMLHTTP'
3114         ]
3115
3116
3117     };
3118 })();/*
3119  * Portions of this file are based on pieces of Yahoo User Interface Library
3120  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3121  * YUI licensed under the BSD License:
3122  * http://developer.yahoo.net/yui/license.txt
3123  * <script type="text/javascript">
3124  *
3125  */
3126
3127 Roo.lib.Region = function(t, r, b, l) {
3128     this.top = t;
3129     this[1] = t;
3130     this.right = r;
3131     this.bottom = b;
3132     this.left = l;
3133     this[0] = l;
3134 };
3135
3136
3137 Roo.lib.Region.prototype = {
3138     contains : function(region) {
3139         return ( region.left >= this.left &&
3140                  region.right <= this.right &&
3141                  region.top >= this.top &&
3142                  region.bottom <= this.bottom    );
3143
3144     },
3145
3146     getArea : function() {
3147         return ( (this.bottom - this.top) * (this.right - this.left) );
3148     },
3149
3150     intersect : function(region) {
3151         var t = Math.max(this.top, region.top);
3152         var r = Math.min(this.right, region.right);
3153         var b = Math.min(this.bottom, region.bottom);
3154         var l = Math.max(this.left, region.left);
3155
3156         if (b >= t && r >= l) {
3157             return new Roo.lib.Region(t, r, b, l);
3158         } else {
3159             return null;
3160         }
3161     },
3162     union : function(region) {
3163         var t = Math.min(this.top, region.top);
3164         var r = Math.max(this.right, region.right);
3165         var b = Math.max(this.bottom, region.bottom);
3166         var l = Math.min(this.left, region.left);
3167
3168         return new Roo.lib.Region(t, r, b, l);
3169     },
3170
3171     adjust : function(t, l, b, r) {
3172         this.top += t;
3173         this.left += l;
3174         this.right += r;
3175         this.bottom += b;
3176         return this;
3177     }
3178 };
3179
3180 Roo.lib.Region.getRegion = function(el) {
3181     var p = Roo.lib.Dom.getXY(el);
3182
3183     var t = p[1];
3184     var r = p[0] + el.offsetWidth;
3185     var b = p[1] + el.offsetHeight;
3186     var l = p[0];
3187
3188     return new Roo.lib.Region(t, r, b, l);
3189 };
3190 /*
3191  * Portions of this file are based on pieces of Yahoo User Interface Library
3192  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3193  * YUI licensed under the BSD License:
3194  * http://developer.yahoo.net/yui/license.txt
3195  * <script type="text/javascript">
3196  *
3197  */
3198 //@@dep Roo.lib.Region
3199
3200
3201 Roo.lib.Point = function(x, y) {
3202     if (x instanceof Array) {
3203         y = x[1];
3204         x = x[0];
3205     }
3206     this.x = this.right = this.left = this[0] = x;
3207     this.y = this.top = this.bottom = this[1] = y;
3208 };
3209
3210 Roo.lib.Point.prototype = new Roo.lib.Region();
3211 /*
3212  * Portions of this file are based on pieces of Yahoo User Interface Library
3213  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3214  * YUI licensed under the BSD License:
3215  * http://developer.yahoo.net/yui/license.txt
3216  * <script type="text/javascript">
3217  *
3218  */
3219  
3220 (function() {   
3221
3222     Roo.lib.Anim = {
3223         scroll : function(el, args, duration, easing, cb, scope) {
3224             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3225         },
3226
3227         motion : function(el, args, duration, easing, cb, scope) {
3228             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3229         },
3230
3231         color : function(el, args, duration, easing, cb, scope) {
3232             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3233         },
3234
3235         run : function(el, args, duration, easing, cb, scope, type) {
3236             type = type || Roo.lib.AnimBase;
3237             if (typeof easing == "string") {
3238                 easing = Roo.lib.Easing[easing];
3239             }
3240             var anim = new type(el, args, duration, easing);
3241             anim.animateX(function() {
3242                 Roo.callback(cb, scope);
3243             });
3244             return anim;
3245         }
3246     };
3247 })();/*
3248  * Portions of this file are based on pieces of Yahoo User Interface Library
3249  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3250  * YUI licensed under the BSD License:
3251  * http://developer.yahoo.net/yui/license.txt
3252  * <script type="text/javascript">
3253  *
3254  */
3255
3256 (function() {    
3257     var libFlyweight;
3258     
3259     function fly(el) {
3260         if (!libFlyweight) {
3261             libFlyweight = new Roo.Element.Flyweight();
3262         }
3263         libFlyweight.dom = el;
3264         return libFlyweight;
3265     }
3266
3267     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3268     
3269    
3270     
3271     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3272         if (el) {
3273             this.init(el, attributes, duration, method);
3274         }
3275     };
3276
3277     Roo.lib.AnimBase.fly = fly;
3278     
3279     
3280     
3281     Roo.lib.AnimBase.prototype = {
3282
3283         toString: function() {
3284             var el = this.getEl();
3285             var id = el.id || el.tagName;
3286             return ("Anim " + id);
3287         },
3288
3289         patterns: {
3290             noNegatives:        /width|height|opacity|padding/i,
3291             offsetAttribute:  /^((width|height)|(top|left))$/,
3292             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3293             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3294         },
3295
3296
3297         doMethod: function(attr, start, end) {
3298             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3299         },
3300
3301
3302         setAttribute: function(attr, val, unit) {
3303             if (this.patterns.noNegatives.test(attr)) {
3304                 val = (val > 0) ? val : 0;
3305             }
3306
3307             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3308         },
3309
3310
3311         getAttribute: function(attr) {
3312             var el = this.getEl();
3313             var val = fly(el).getStyle(attr);
3314
3315             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3316                 return parseFloat(val);
3317             }
3318
3319             var a = this.patterns.offsetAttribute.exec(attr) || [];
3320             var pos = !!( a[3] );
3321             var box = !!( a[2] );
3322
3323
3324             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3325                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3326             } else {
3327                 val = 0;
3328             }
3329
3330             return val;
3331         },
3332
3333
3334         getDefaultUnit: function(attr) {
3335             if (this.patterns.defaultUnit.test(attr)) {
3336                 return 'px';
3337             }
3338
3339             return '';
3340         },
3341
3342         animateX : function(callback, scope) {
3343             var f = function() {
3344                 this.onComplete.removeListener(f);
3345                 if (typeof callback == "function") {
3346                     callback.call(scope || this, this);
3347                 }
3348             };
3349             this.onComplete.addListener(f, this);
3350             this.animate();
3351         },
3352
3353
3354         setRuntimeAttribute: function(attr) {
3355             var start;
3356             var end;
3357             var attributes = this.attributes;
3358
3359             this.runtimeAttributes[attr] = {};
3360
3361             var isset = function(prop) {
3362                 return (typeof prop !== 'undefined');
3363             };
3364
3365             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3366                 return false;
3367             }
3368
3369             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3370
3371
3372             if (isset(attributes[attr]['to'])) {
3373                 end = attributes[attr]['to'];
3374             } else if (isset(attributes[attr]['by'])) {
3375                 if (start.constructor == Array) {
3376                     end = [];
3377                     for (var i = 0, len = start.length; i < len; ++i) {
3378                         end[i] = start[i] + attributes[attr]['by'][i];
3379                     }
3380                 } else {
3381                     end = start + attributes[attr]['by'];
3382                 }
3383             }
3384
3385             this.runtimeAttributes[attr].start = start;
3386             this.runtimeAttributes[attr].end = end;
3387
3388
3389             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3390         },
3391
3392
3393         init: function(el, attributes, duration, method) {
3394
3395             var isAnimated = false;
3396
3397
3398             var startTime = null;
3399
3400
3401             var actualFrames = 0;
3402
3403
3404             el = Roo.getDom(el);
3405
3406
3407             this.attributes = attributes || {};
3408
3409
3410             this.duration = duration || 1;
3411
3412
3413             this.method = method || Roo.lib.Easing.easeNone;
3414
3415
3416             this.useSeconds = true;
3417
3418
3419             this.currentFrame = 0;
3420
3421
3422             this.totalFrames = Roo.lib.AnimMgr.fps;
3423
3424
3425             this.getEl = function() {
3426                 return el;
3427             };
3428
3429
3430             this.isAnimated = function() {
3431                 return isAnimated;
3432             };
3433
3434
3435             this.getStartTime = function() {
3436                 return startTime;
3437             };
3438
3439             this.runtimeAttributes = {};
3440
3441
3442             this.animate = function() {
3443                 if (this.isAnimated()) {
3444                     return false;
3445                 }
3446
3447                 this.currentFrame = 0;
3448
3449                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3450
3451                 Roo.lib.AnimMgr.registerElement(this);
3452             };
3453
3454
3455             this.stop = function(finish) {
3456                 if (finish) {
3457                     this.currentFrame = this.totalFrames;
3458                     this._onTween.fire();
3459                 }
3460                 Roo.lib.AnimMgr.stop(this);
3461             };
3462
3463             var onStart = function() {
3464                 this.onStart.fire();
3465
3466                 this.runtimeAttributes = {};
3467                 for (var attr in this.attributes) {
3468                     this.setRuntimeAttribute(attr);
3469                 }
3470
3471                 isAnimated = true;
3472                 actualFrames = 0;
3473                 startTime = new Date();
3474             };
3475
3476
3477             var onTween = function() {
3478                 var data = {
3479                     duration: new Date() - this.getStartTime(),
3480                     currentFrame: this.currentFrame
3481                 };
3482
3483                 data.toString = function() {
3484                     return (
3485                             'duration: ' + data.duration +
3486                             ', currentFrame: ' + data.currentFrame
3487                             );
3488                 };
3489
3490                 this.onTween.fire(data);
3491
3492                 var runtimeAttributes = this.runtimeAttributes;
3493
3494                 for (var attr in runtimeAttributes) {
3495                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3496                 }
3497
3498                 actualFrames += 1;
3499             };
3500
3501             var onComplete = function() {
3502                 var actual_duration = (new Date() - startTime) / 1000 ;
3503
3504                 var data = {
3505                     duration: actual_duration,
3506                     frames: actualFrames,
3507                     fps: actualFrames / actual_duration
3508                 };
3509
3510                 data.toString = function() {
3511                     return (
3512                             'duration: ' + data.duration +
3513                             ', frames: ' + data.frames +
3514                             ', fps: ' + data.fps
3515                             );
3516                 };
3517
3518                 isAnimated = false;
3519                 actualFrames = 0;
3520                 this.onComplete.fire(data);
3521             };
3522
3523
3524             this._onStart = new Roo.util.Event(this);
3525             this.onStart = new Roo.util.Event(this);
3526             this.onTween = new Roo.util.Event(this);
3527             this._onTween = new Roo.util.Event(this);
3528             this.onComplete = new Roo.util.Event(this);
3529             this._onComplete = new Roo.util.Event(this);
3530             this._onStart.addListener(onStart);
3531             this._onTween.addListener(onTween);
3532             this._onComplete.addListener(onComplete);
3533         }
3534     };
3535 })();
3536 /*
3537  * Portions of this file are based on pieces of Yahoo User Interface Library
3538  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3539  * YUI licensed under the BSD License:
3540  * http://developer.yahoo.net/yui/license.txt
3541  * <script type="text/javascript">
3542  *
3543  */
3544
3545 Roo.lib.AnimMgr = new function() {
3546
3547     var thread = null;
3548
3549
3550     var queue = [];
3551
3552
3553     var tweenCount = 0;
3554
3555
3556     this.fps = 1000;
3557
3558
3559     this.delay = 1;
3560
3561
3562     this.registerElement = function(tween) {
3563         queue[queue.length] = tween;
3564         tweenCount += 1;
3565         tween._onStart.fire();
3566         this.start();
3567     };
3568
3569
3570     this.unRegister = function(tween, index) {
3571         tween._onComplete.fire();
3572         index = index || getIndex(tween);
3573         if (index != -1) {
3574             queue.splice(index, 1);
3575         }
3576
3577         tweenCount -= 1;
3578         if (tweenCount <= 0) {
3579             this.stop();
3580         }
3581     };
3582
3583
3584     this.start = function() {
3585         if (thread === null) {
3586             thread = setInterval(this.run, this.delay);
3587         }
3588     };
3589
3590
3591     this.stop = function(tween) {
3592         if (!tween) {
3593             clearInterval(thread);
3594
3595             for (var i = 0, len = queue.length; i < len; ++i) {
3596                 if (queue[0].isAnimated()) {
3597                     this.unRegister(queue[0], 0);
3598                 }
3599             }
3600
3601             queue = [];
3602             thread = null;
3603             tweenCount = 0;
3604         }
3605         else {
3606             this.unRegister(tween);
3607         }
3608     };
3609
3610
3611     this.run = function() {
3612         for (var i = 0, len = queue.length; i < len; ++i) {
3613             var tween = queue[i];
3614             if (!tween || !tween.isAnimated()) {
3615                 continue;
3616             }
3617
3618             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3619             {
3620                 tween.currentFrame += 1;
3621
3622                 if (tween.useSeconds) {
3623                     correctFrame(tween);
3624                 }
3625                 tween._onTween.fire();
3626             }
3627             else {
3628                 Roo.lib.AnimMgr.stop(tween, i);
3629             }
3630         }
3631     };
3632
3633     var getIndex = function(anim) {
3634         for (var i = 0, len = queue.length; i < len; ++i) {
3635             if (queue[i] == anim) {
3636                 return i;
3637             }
3638         }
3639         return -1;
3640     };
3641
3642
3643     var correctFrame = function(tween) {
3644         var frames = tween.totalFrames;
3645         var frame = tween.currentFrame;
3646         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3647         var elapsed = (new Date() - tween.getStartTime());
3648         var tweak = 0;
3649
3650         if (elapsed < tween.duration * 1000) {
3651             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3652         } else {
3653             tweak = frames - (frame + 1);
3654         }
3655         if (tweak > 0 && isFinite(tweak)) {
3656             if (tween.currentFrame + tweak >= frames) {
3657                 tweak = frames - (frame + 1);
3658             }
3659
3660             tween.currentFrame += tweak;
3661         }
3662     };
3663 };
3664
3665     /*
3666  * Portions of this file are based on pieces of Yahoo User Interface Library
3667  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3668  * YUI licensed under the BSD License:
3669  * http://developer.yahoo.net/yui/license.txt
3670  * <script type="text/javascript">
3671  *
3672  */
3673 Roo.lib.Bezier = new function() {
3674
3675         this.getPosition = function(points, t) {
3676             var n = points.length;
3677             var tmp = [];
3678
3679             for (var i = 0; i < n; ++i) {
3680                 tmp[i] = [points[i][0], points[i][1]];
3681             }
3682
3683             for (var j = 1; j < n; ++j) {
3684                 for (i = 0; i < n - j; ++i) {
3685                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3686                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3687                 }
3688             }
3689
3690             return [ tmp[0][0], tmp[0][1] ];
3691
3692         };
3693     }; 
3694
3695 /**
3696  * @class Roo.lib.Color
3697  * @constructor
3698  * An abstract Color implementation. Concrete Color implementations should use
3699  * an instance of this function as their prototype, and implement the getRGB and
3700  * getHSL functions. getRGB should return an object representing the RGB
3701  * components of this Color, with the red, green, and blue components in the
3702  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3703  * return an object representing the HSL components of this Color, with the hue
3704  * component in the range [0,360), the saturation and lightness components in
3705  * the range [0,100], and the alpha component in the range [0,1].
3706  *
3707  *
3708  * Color.js
3709  *
3710  * Functions for Color handling and processing.
3711  *
3712  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3713  *
3714  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3715  * rights to this program, with the intention of it becoming part of the public
3716  * domain. Because this program is released into the public domain, it comes with
3717  * no warranty either expressed or implied, to the extent permitted by law.
3718  * 
3719  * For more free and public domain JavaScript code by the same author, visit:
3720  * http://www.safalra.com/web-design/javascript/
3721  * 
3722  */
3723 Roo.lib.Color = function() { }
3724
3725
3726 Roo.apply(Roo.lib.Color.prototype, {
3727   
3728   rgb : null,
3729   hsv : null,
3730   hsl : null,
3731   
3732   /**
3733    * getIntegerRGB
3734    * @return {Object} an object representing the RGBA components of this Color. The red,
3735    * green, and blue components are converted to integers in the range [0,255].
3736    * The alpha is a value in the range [0,1].
3737    */
3738   getIntegerRGB : function(){
3739
3740     // get the RGB components of this Color
3741     var rgb = this.getRGB();
3742
3743     // return the integer components
3744     return {
3745       'r' : Math.round(rgb.r),
3746       'g' : Math.round(rgb.g),
3747       'b' : Math.round(rgb.b),
3748       'a' : rgb.a
3749     };
3750
3751   },
3752
3753   /**
3754    * getPercentageRGB
3755    * @return {Object} an object representing the RGBA components of this Color. The red,
3756    * green, and blue components are converted to numbers in the range [0,100].
3757    * The alpha is a value in the range [0,1].
3758    */
3759   getPercentageRGB : function(){
3760
3761     // get the RGB components of this Color
3762     var rgb = this.getRGB();
3763
3764     // return the percentage components
3765     return {
3766       'r' : 100 * rgb.r / 255,
3767       'g' : 100 * rgb.g / 255,
3768       'b' : 100 * rgb.b / 255,
3769       'a' : rgb.a
3770     };
3771
3772   },
3773
3774   /**
3775    * getCSSHexadecimalRGB
3776    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3777    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3778    * are two-digit hexadecimal numbers.
3779    */
3780   getCSSHexadecimalRGB : function()
3781   {
3782
3783     // get the integer RGB components
3784     var rgb = this.getIntegerRGB();
3785
3786     // determine the hexadecimal equivalents
3787     var r16 = rgb.r.toString(16);
3788     var g16 = rgb.g.toString(16);
3789     var b16 = rgb.b.toString(16);
3790
3791     // return the CSS RGB Color value
3792     return '#'
3793         + (r16.length == 2 ? r16 : '0' + r16)
3794         + (g16.length == 2 ? g16 : '0' + g16)
3795         + (b16.length == 2 ? b16 : '0' + b16);
3796
3797   },
3798
3799   /**
3800    * getCSSIntegerRGB
3801    * @return {String} a string representing this Color as a CSS integer RGB Color
3802    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3803    * are integers in the range [0,255].
3804    */
3805   getCSSIntegerRGB : function(){
3806
3807     // get the integer RGB components
3808     var rgb = this.getIntegerRGB();
3809
3810     // return the CSS RGB Color value
3811     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3812
3813   },
3814
3815   /**
3816    * getCSSIntegerRGBA
3817    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3818    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3819    * b are integers in the range [0,255] and a is in the range [0,1].
3820    */
3821   getCSSIntegerRGBA : function(){
3822
3823     // get the integer RGB components
3824     var rgb = this.getIntegerRGB();
3825
3826     // return the CSS integer RGBA Color value
3827     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3828
3829   },
3830
3831   /**
3832    * getCSSPercentageRGB
3833    * @return {String} a string representing this Color as a CSS percentage RGB Color
3834    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3835    * b are in the range [0,100].
3836    */
3837   getCSSPercentageRGB : function(){
3838
3839     // get the percentage RGB components
3840     var rgb = this.getPercentageRGB();
3841
3842     // return the CSS RGB Color value
3843     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3844
3845   },
3846
3847   /**
3848    * getCSSPercentageRGBA
3849    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3850    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3851    * and b are in the range [0,100] and a is in the range [0,1].
3852    */
3853   getCSSPercentageRGBA : function(){
3854
3855     // get the percentage RGB components
3856     var rgb = this.getPercentageRGB();
3857
3858     // return the CSS percentage RGBA Color value
3859     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3860
3861   },
3862
3863   /**
3864    * getCSSHSL
3865    * @return {String} a string representing this Color as a CSS HSL Color value - that
3866    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3867    * s and l are in the range [0,100].
3868    */
3869   getCSSHSL : function(){
3870
3871     // get the HSL components
3872     var hsl = this.getHSL();
3873
3874     // return the CSS HSL Color value
3875     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3876
3877   },
3878
3879   /**
3880    * getCSSHSLA
3881    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3882    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3883    * s and l are in the range [0,100], and a is in the range [0,1].
3884    */
3885   getCSSHSLA : function(){
3886
3887     // get the HSL components
3888     var hsl = this.getHSL();
3889
3890     // return the CSS HSL Color value
3891     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3892
3893   },
3894
3895   /**
3896    * Sets the Color of the specified node to this Color. This functions sets
3897    * the CSS 'color' property for the node. The parameter is:
3898    * 
3899    * @param {DomElement} node - the node whose Color should be set
3900    */
3901   setNodeColor : function(node){
3902
3903     // set the Color of the node
3904     node.style.color = this.getCSSHexadecimalRGB();
3905
3906   },
3907
3908   /**
3909    * Sets the background Color of the specified node to this Color. This
3910    * functions sets the CSS 'background-color' property for the node. The
3911    * parameter is:
3912    *
3913    * @param {DomElement} node - the node whose background Color should be set
3914    */
3915   setNodeBackgroundColor : function(node){
3916
3917     // set the background Color of the node
3918     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3919
3920   },
3921   // convert between formats..
3922   toRGB: function()
3923   {
3924     var r = this.getIntegerRGB();
3925     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3926     
3927   },
3928   toHSL : function()
3929   {
3930      var hsl = this.getHSL();
3931   // return the CSS HSL Color value
3932     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3933     
3934   },
3935   
3936   toHSV : function()
3937   {
3938     var rgb = this.toRGB();
3939     var hsv = rgb.getHSV();
3940    // return the CSS HSL Color value
3941     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3942     
3943   },
3944   
3945   // modify  v = 0 ... 1 (eg. 0.5)
3946   saturate : function(v)
3947   {
3948       var rgb = this.toRGB();
3949       var hsv = rgb.getHSV();
3950       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3951       
3952   
3953   },
3954   
3955    
3956   /**
3957    * getRGB
3958    * @return {Object} the RGB and alpha components of this Color as an object with r,
3959    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3960    * the range [0,1].
3961    */
3962   getRGB: function(){
3963    
3964     // return the RGB components
3965     return {
3966       'r' : this.rgb.r,
3967       'g' : this.rgb.g,
3968       'b' : this.rgb.b,
3969       'a' : this.alpha
3970     };
3971
3972   },
3973
3974   /**
3975    * getHSV
3976    * @return {Object} the HSV and alpha components of this Color as an object with h,
3977    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3978    * [0,100], and a is in the range [0,1].
3979    */
3980   getHSV : function()
3981   {
3982     
3983     // calculate the HSV components if necessary
3984     if (this.hsv == null) {
3985       this.calculateHSV();
3986     }
3987
3988     // return the HSV components
3989     return {
3990       'h' : this.hsv.h,
3991       's' : this.hsv.s,
3992       'v' : this.hsv.v,
3993       'a' : this.alpha
3994     };
3995
3996   },
3997
3998   /**
3999    * getHSL
4000    * @return {Object} the HSL and alpha components of this Color as an object with h,
4001    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4002    * [0,100], and a is in the range [0,1].
4003    */
4004   getHSL : function(){
4005     
4006      
4007     // calculate the HSV components if necessary
4008     if (this.hsl == null) { this.calculateHSL(); }
4009
4010     // return the HSL components
4011     return {
4012       'h' : this.hsl.h,
4013       's' : this.hsl.s,
4014       'l' : this.hsl.l,
4015       'a' : this.alpha
4016     };
4017
4018   }
4019   
4020
4021 });
4022
4023
4024 /**
4025  * @class Roo.lib.RGBColor
4026  * @extends Roo.lib.Color
4027  * Creates a Color specified in the RGB Color space, with an optional alpha
4028  * component. The parameters are:
4029  * @constructor
4030  * 
4031
4032  * @param {Number} r - the red component, clipped to the range [0,255]
4033  * @param {Number} g - the green component, clipped to the range [0,255]
4034  * @param {Number} b - the blue component, clipped to the range [0,255]
4035  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4036  *     optional and defaults to 1
4037  */
4038 Roo.lib.RGBColor = function (r, g, b, a){
4039
4040   // store the alpha component after clipping it if necessary
4041   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4042
4043   // store the RGB components after clipping them if necessary
4044   this.rgb =
4045       {
4046         'r' : Math.max(0, Math.min(255, r)),
4047         'g' : Math.max(0, Math.min(255, g)),
4048         'b' : Math.max(0, Math.min(255, b))
4049       };
4050
4051   // initialise the HSV and HSL components to null
4052   
4053
4054   /* 
4055    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4056    * range [0,360). The parameters are:
4057    *
4058    * maximum - the maximum of the RGB component values
4059    * range   - the range of the RGB component values
4060    */
4061    
4062
4063 }
4064 // this does an 'exteds'
4065 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4066
4067   
4068     getHue  : function(maximum, range)
4069     {
4070       var rgb = this.rgb;
4071        
4072       // check whether the range is zero
4073       if (range == 0){
4074   
4075         // set the hue to zero (any hue is acceptable as the Color is grey)
4076         var hue = 0;
4077   
4078       }else{
4079   
4080         // determine which of the components has the highest value and set the hue
4081         switch (maximum){
4082   
4083           // red has the highest value
4084           case rgb.r:
4085             var hue = (rgb.g - rgb.b) / range * 60;
4086             if (hue < 0) { hue += 360; }
4087             break;
4088   
4089           // green has the highest value
4090           case rgb.g:
4091             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4092             break;
4093   
4094           // blue has the highest value
4095           case rgb.b:
4096             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4097             break;
4098   
4099         }
4100   
4101       }
4102   
4103       // return the hue
4104       return hue;
4105   
4106     },
4107
4108   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4109    * be returned be the getHSV function.
4110    */
4111    calculateHSV : function(){
4112     var rgb = this.rgb;
4113     // get the maximum and range of the RGB component values
4114     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4115     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4116
4117     // store the HSV components
4118     this.hsv =
4119         {
4120           'h' : this.getHue(maximum, range),
4121           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4122           'v' : maximum / 2.55
4123         };
4124
4125   },
4126
4127   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4128    * be returned be the getHSL function.
4129    */
4130    calculateHSL : function(){
4131     var rgb = this.rgb;
4132     // get the maximum and range of the RGB component values
4133     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4134     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4135
4136     // determine the lightness in the range [0,1]
4137     var l = maximum / 255 - range / 510;
4138
4139     // store the HSL components
4140     this.hsl =
4141         {
4142           'h' : this.getHue(maximum, range),
4143           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4144           'l' : 100 * l
4145         };
4146
4147   }
4148
4149 });
4150
4151 /**
4152  * @class Roo.lib.HSVColor
4153  * @extends Roo.lib.Color
4154  * Creates a Color specified in the HSV Color space, with an optional alpha
4155  * component. The parameters are:
4156  * @constructor
4157  *
4158  * @param {Number} h - the hue component, wrapped to the range [0,360)
4159  * @param {Number} s - the saturation component, clipped to the range [0,100]
4160  * @param {Number} v - the value component, clipped to the range [0,100]
4161  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4162  *     optional and defaults to 1
4163  */
4164 Roo.lib.HSVColor = function (h, s, v, a){
4165
4166   // store the alpha component after clipping it if necessary
4167   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4168
4169   // store the HSV components after clipping or wrapping them if necessary
4170   this.hsv =
4171       {
4172         'h' : (h % 360 + 360) % 360,
4173         's' : Math.max(0, Math.min(100, s)),
4174         'v' : Math.max(0, Math.min(100, v))
4175       };
4176
4177   // initialise the RGB and HSL components to null
4178   this.rgb = null;
4179   this.hsl = null;
4180 }
4181
4182 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4183   /* Calculates and stores the RGB components of this HSVColor so that they can
4184    * be returned be the getRGB function.
4185    */
4186   calculateRGB: function ()
4187   {
4188     var hsv = this.hsv;
4189     // check whether the saturation is zero
4190     if (hsv.s == 0){
4191
4192       // set the Color to the appropriate shade of grey
4193       var r = hsv.v;
4194       var g = hsv.v;
4195       var b = hsv.v;
4196
4197     }else{
4198
4199       // set some temporary values
4200       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4201       var p  = hsv.v * (1 - hsv.s / 100);
4202       var q  = hsv.v * (1 - hsv.s / 100 * f);
4203       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4204
4205       // set the RGB Color components to their temporary values
4206       switch (Math.floor(hsv.h / 60)){
4207         case 0: var r = hsv.v; var g = t; var b = p; break;
4208         case 1: var r = q; var g = hsv.v; var b = p; break;
4209         case 2: var r = p; var g = hsv.v; var b = t; break;
4210         case 3: var r = p; var g = q; var b = hsv.v; break;
4211         case 4: var r = t; var g = p; var b = hsv.v; break;
4212         case 5: var r = hsv.v; var g = p; var b = q; break;
4213       }
4214
4215     }
4216
4217     // store the RGB components
4218     this.rgb =
4219         {
4220           'r' : r * 2.55,
4221           'g' : g * 2.55,
4222           'b' : b * 2.55
4223         };
4224
4225   },
4226
4227   /* Calculates and stores the HSL components of this HSVColor so that they can
4228    * be returned be the getHSL function.
4229    */
4230   calculateHSL : function (){
4231
4232     var hsv = this.hsv;
4233     // determine the lightness in the range [0,100]
4234     var l = (2 - hsv.s / 100) * hsv.v / 2;
4235
4236     // store the HSL components
4237     this.hsl =
4238         {
4239           'h' : hsv.h,
4240           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4241           'l' : l
4242         };
4243
4244     // correct a division-by-zero error
4245     if (isNaN(hsl.s)) { hsl.s = 0; }
4246
4247   } 
4248  
4249
4250 });
4251  
4252
4253 /**
4254  * @class Roo.lib.HSLColor
4255  * @extends Roo.lib.Color
4256  *
4257  * @constructor
4258  * Creates a Color specified in the HSL Color space, with an optional alpha
4259  * component. The parameters are:
4260  *
4261  * @param {Number} h - the hue component, wrapped to the range [0,360)
4262  * @param {Number} s - the saturation component, clipped to the range [0,100]
4263  * @param {Number} l - the lightness component, clipped to the range [0,100]
4264  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4265  *     optional and defaults to 1
4266  */
4267
4268 Roo.lib.HSLColor = function(h, s, l, a){
4269
4270   // store the alpha component after clipping it if necessary
4271   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4272
4273   // store the HSL components after clipping or wrapping them if necessary
4274   this.hsl =
4275       {
4276         'h' : (h % 360 + 360) % 360,
4277         's' : Math.max(0, Math.min(100, s)),
4278         'l' : Math.max(0, Math.min(100, l))
4279       };
4280
4281   // initialise the RGB and HSV components to null
4282 }
4283
4284 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4285
4286   /* Calculates and stores the RGB components of this HSLColor so that they can
4287    * be returned be the getRGB function.
4288    */
4289   calculateRGB: function (){
4290
4291     // check whether the saturation is zero
4292     if (this.hsl.s == 0){
4293
4294       // store the RGB components representing the appropriate shade of grey
4295       this.rgb =
4296           {
4297             'r' : this.hsl.l * 2.55,
4298             'g' : this.hsl.l * 2.55,
4299             'b' : this.hsl.l * 2.55
4300           };
4301
4302     }else{
4303
4304       // set some temporary values
4305       var p = this.hsl.l < 50
4306             ? this.hsl.l * (1 + hsl.s / 100)
4307             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4308       var q = 2 * hsl.l - p;
4309
4310       // initialise the RGB components
4311       this.rgb =
4312           {
4313             'r' : (h + 120) / 60 % 6,
4314             'g' : h / 60,
4315             'b' : (h + 240) / 60 % 6
4316           };
4317
4318       // loop over the RGB components
4319       for (var key in this.rgb){
4320
4321         // ensure that the property is not inherited from the root object
4322         if (this.rgb.hasOwnProperty(key)){
4323
4324           // set the component to its value in the range [0,100]
4325           if (this.rgb[key] < 1){
4326             this.rgb[key] = q + (p - q) * this.rgb[key];
4327           }else if (this.rgb[key] < 3){
4328             this.rgb[key] = p;
4329           }else if (this.rgb[key] < 4){
4330             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4331           }else{
4332             this.rgb[key] = q;
4333           }
4334
4335           // set the component to its value in the range [0,255]
4336           this.rgb[key] *= 2.55;
4337
4338         }
4339
4340       }
4341
4342     }
4343
4344   },
4345
4346   /* Calculates and stores the HSV components of this HSLColor so that they can
4347    * be returned be the getHSL function.
4348    */
4349    calculateHSV : function(){
4350
4351     // set a temporary value
4352     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4353
4354     // store the HSV components
4355     this.hsv =
4356         {
4357           'h' : this.hsl.h,
4358           's' : 200 * t / (this.hsl.l + t),
4359           'v' : t + this.hsl.l
4360         };
4361
4362     // correct a division-by-zero error
4363     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4364
4365   }
4366  
4367
4368 });
4369 /*
4370  * Portions of this file are based on pieces of Yahoo User Interface Library
4371  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4372  * YUI licensed under the BSD License:
4373  * http://developer.yahoo.net/yui/license.txt
4374  * <script type="text/javascript">
4375  *
4376  */
4377 (function() {
4378
4379     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4380         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4381     };
4382
4383     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4384
4385     var fly = Roo.lib.AnimBase.fly;
4386     var Y = Roo.lib;
4387     var superclass = Y.ColorAnim.superclass;
4388     var proto = Y.ColorAnim.prototype;
4389
4390     proto.toString = function() {
4391         var el = this.getEl();
4392         var id = el.id || el.tagName;
4393         return ("ColorAnim " + id);
4394     };
4395
4396     proto.patterns.color = /color$/i;
4397     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4398     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4399     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4400     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4401
4402
4403     proto.parseColor = function(s) {
4404         if (s.length == 3) {
4405             return s;
4406         }
4407
4408         var c = this.patterns.hex.exec(s);
4409         if (c && c.length == 4) {
4410             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4411         }
4412
4413         c = this.patterns.rgb.exec(s);
4414         if (c && c.length == 4) {
4415             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4416         }
4417
4418         c = this.patterns.hex3.exec(s);
4419         if (c && c.length == 4) {
4420             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4421         }
4422
4423         return null;
4424     };
4425     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4426     proto.getAttribute = function(attr) {
4427         var el = this.getEl();
4428         if (this.patterns.color.test(attr)) {
4429             var val = fly(el).getStyle(attr);
4430
4431             if (this.patterns.transparent.test(val)) {
4432                 var parent = el.parentNode;
4433                 val = fly(parent).getStyle(attr);
4434
4435                 while (parent && this.patterns.transparent.test(val)) {
4436                     parent = parent.parentNode;
4437                     val = fly(parent).getStyle(attr);
4438                     if (parent.tagName.toUpperCase() == 'HTML') {
4439                         val = '#fff';
4440                     }
4441                 }
4442             }
4443         } else {
4444             val = superclass.getAttribute.call(this, attr);
4445         }
4446
4447         return val;
4448     };
4449     proto.getAttribute = function(attr) {
4450         var el = this.getEl();
4451         if (this.patterns.color.test(attr)) {
4452             var val = fly(el).getStyle(attr);
4453
4454             if (this.patterns.transparent.test(val)) {
4455                 var parent = el.parentNode;
4456                 val = fly(parent).getStyle(attr);
4457
4458                 while (parent && this.patterns.transparent.test(val)) {
4459                     parent = parent.parentNode;
4460                     val = fly(parent).getStyle(attr);
4461                     if (parent.tagName.toUpperCase() == 'HTML') {
4462                         val = '#fff';
4463                     }
4464                 }
4465             }
4466         } else {
4467             val = superclass.getAttribute.call(this, attr);
4468         }
4469
4470         return val;
4471     };
4472
4473     proto.doMethod = function(attr, start, end) {
4474         var val;
4475
4476         if (this.patterns.color.test(attr)) {
4477             val = [];
4478             for (var i = 0, len = start.length; i < len; ++i) {
4479                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4480             }
4481
4482             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4483         }
4484         else {
4485             val = superclass.doMethod.call(this, attr, start, end);
4486         }
4487
4488         return val;
4489     };
4490
4491     proto.setRuntimeAttribute = function(attr) {
4492         superclass.setRuntimeAttribute.call(this, attr);
4493
4494         if (this.patterns.color.test(attr)) {
4495             var attributes = this.attributes;
4496             var start = this.parseColor(this.runtimeAttributes[attr].start);
4497             var end = this.parseColor(this.runtimeAttributes[attr].end);
4498
4499             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4500                 end = this.parseColor(attributes[attr].by);
4501
4502                 for (var i = 0, len = start.length; i < len; ++i) {
4503                     end[i] = start[i] + end[i];
4504                 }
4505             }
4506
4507             this.runtimeAttributes[attr].start = start;
4508             this.runtimeAttributes[attr].end = end;
4509         }
4510     };
4511 })();
4512
4513 /*
4514  * Portions of this file are based on pieces of Yahoo User Interface Library
4515  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4516  * YUI licensed under the BSD License:
4517  * http://developer.yahoo.net/yui/license.txt
4518  * <script type="text/javascript">
4519  *
4520  */
4521 Roo.lib.Easing = {
4522
4523
4524     easeNone: function (t, b, c, d) {
4525         return c * t / d + b;
4526     },
4527
4528
4529     easeIn: function (t, b, c, d) {
4530         return c * (t /= d) * t + b;
4531     },
4532
4533
4534     easeOut: function (t, b, c, d) {
4535         return -c * (t /= d) * (t - 2) + b;
4536     },
4537
4538
4539     easeBoth: function (t, b, c, d) {
4540         if ((t /= d / 2) < 1) {
4541             return c / 2 * t * t + b;
4542         }
4543
4544         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4545     },
4546
4547
4548     easeInStrong: function (t, b, c, d) {
4549         return c * (t /= d) * t * t * t + b;
4550     },
4551
4552
4553     easeOutStrong: function (t, b, c, d) {
4554         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4555     },
4556
4557
4558     easeBothStrong: function (t, b, c, d) {
4559         if ((t /= d / 2) < 1) {
4560             return c / 2 * t * t * t * t + b;
4561         }
4562
4563         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4564     },
4565
4566
4567
4568     elasticIn: function (t, b, c, d, a, p) {
4569         if (t == 0) {
4570             return b;
4571         }
4572         if ((t /= d) == 1) {
4573             return b + c;
4574         }
4575         if (!p) {
4576             p = d * .3;
4577         }
4578
4579         if (!a || a < Math.abs(c)) {
4580             a = c;
4581             var s = p / 4;
4582         }
4583         else {
4584             var s = p / (2 * Math.PI) * Math.asin(c / a);
4585         }
4586
4587         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4588     },
4589
4590
4591     elasticOut: function (t, b, c, d, a, p) {
4592         if (t == 0) {
4593             return b;
4594         }
4595         if ((t /= d) == 1) {
4596             return b + c;
4597         }
4598         if (!p) {
4599             p = d * .3;
4600         }
4601
4602         if (!a || a < Math.abs(c)) {
4603             a = c;
4604             var s = p / 4;
4605         }
4606         else {
4607             var s = p / (2 * Math.PI) * Math.asin(c / a);
4608         }
4609
4610         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4611     },
4612
4613
4614     elasticBoth: function (t, b, c, d, a, p) {
4615         if (t == 0) {
4616             return b;
4617         }
4618
4619         if ((t /= d / 2) == 2) {
4620             return b + c;
4621         }
4622
4623         if (!p) {
4624             p = d * (.3 * 1.5);
4625         }
4626
4627         if (!a || a < Math.abs(c)) {
4628             a = c;
4629             var s = p / 4;
4630         }
4631         else {
4632             var s = p / (2 * Math.PI) * Math.asin(c / a);
4633         }
4634
4635         if (t < 1) {
4636             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4637                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4638         }
4639         return a * Math.pow(2, -10 * (t -= 1)) *
4640                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4641     },
4642
4643
4644
4645     backIn: function (t, b, c, d, s) {
4646         if (typeof s == 'undefined') {
4647             s = 1.70158;
4648         }
4649         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4650     },
4651
4652
4653     backOut: function (t, b, c, d, s) {
4654         if (typeof s == 'undefined') {
4655             s = 1.70158;
4656         }
4657         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4658     },
4659
4660
4661     backBoth: function (t, b, c, d, s) {
4662         if (typeof s == 'undefined') {
4663             s = 1.70158;
4664         }
4665
4666         if ((t /= d / 2 ) < 1) {
4667             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4668         }
4669         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4670     },
4671
4672
4673     bounceIn: function (t, b, c, d) {
4674         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4675     },
4676
4677
4678     bounceOut: function (t, b, c, d) {
4679         if ((t /= d) < (1 / 2.75)) {
4680             return c * (7.5625 * t * t) + b;
4681         } else if (t < (2 / 2.75)) {
4682             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4683         } else if (t < (2.5 / 2.75)) {
4684             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4685         }
4686         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4687     },
4688
4689
4690     bounceBoth: function (t, b, c, d) {
4691         if (t < d / 2) {
4692             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4693         }
4694         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4695     }
4696 };/*
4697  * Portions of this file are based on pieces of Yahoo User Interface Library
4698  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4699  * YUI licensed under the BSD License:
4700  * http://developer.yahoo.net/yui/license.txt
4701  * <script type="text/javascript">
4702  *
4703  */
4704     (function() {
4705         Roo.lib.Motion = function(el, attributes, duration, method) {
4706             if (el) {
4707                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4708             }
4709         };
4710
4711         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4712
4713
4714         var Y = Roo.lib;
4715         var superclass = Y.Motion.superclass;
4716         var proto = Y.Motion.prototype;
4717
4718         proto.toString = function() {
4719             var el = this.getEl();
4720             var id = el.id || el.tagName;
4721             return ("Motion " + id);
4722         };
4723
4724         proto.patterns.points = /^points$/i;
4725
4726         proto.setAttribute = function(attr, val, unit) {
4727             if (this.patterns.points.test(attr)) {
4728                 unit = unit || 'px';
4729                 superclass.setAttribute.call(this, 'left', val[0], unit);
4730                 superclass.setAttribute.call(this, 'top', val[1], unit);
4731             } else {
4732                 superclass.setAttribute.call(this, attr, val, unit);
4733             }
4734         };
4735
4736         proto.getAttribute = function(attr) {
4737             if (this.patterns.points.test(attr)) {
4738                 var val = [
4739                         superclass.getAttribute.call(this, 'left'),
4740                         superclass.getAttribute.call(this, 'top')
4741                         ];
4742             } else {
4743                 val = superclass.getAttribute.call(this, attr);
4744             }
4745
4746             return val;
4747         };
4748
4749         proto.doMethod = function(attr, start, end) {
4750             var val = null;
4751
4752             if (this.patterns.points.test(attr)) {
4753                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4754                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4755             } else {
4756                 val = superclass.doMethod.call(this, attr, start, end);
4757             }
4758             return val;
4759         };
4760
4761         proto.setRuntimeAttribute = function(attr) {
4762             if (this.patterns.points.test(attr)) {
4763                 var el = this.getEl();
4764                 var attributes = this.attributes;
4765                 var start;
4766                 var control = attributes['points']['control'] || [];
4767                 var end;
4768                 var i, len;
4769
4770                 if (control.length > 0 && !(control[0] instanceof Array)) {
4771                     control = [control];
4772                 } else {
4773                     var tmp = [];
4774                     for (i = 0,len = control.length; i < len; ++i) {
4775                         tmp[i] = control[i];
4776                     }
4777                     control = tmp;
4778                 }
4779
4780                 Roo.fly(el).position();
4781
4782                 if (isset(attributes['points']['from'])) {
4783                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4784                 }
4785                 else {
4786                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4787                 }
4788
4789                 start = this.getAttribute('points');
4790
4791
4792                 if (isset(attributes['points']['to'])) {
4793                     end = translateValues.call(this, attributes['points']['to'], start);
4794
4795                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4796                     for (i = 0,len = control.length; i < len; ++i) {
4797                         control[i] = translateValues.call(this, control[i], start);
4798                     }
4799
4800
4801                 } else if (isset(attributes['points']['by'])) {
4802                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4803
4804                     for (i = 0,len = control.length; i < len; ++i) {
4805                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4806                     }
4807                 }
4808
4809                 this.runtimeAttributes[attr] = [start];
4810
4811                 if (control.length > 0) {
4812                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4813                 }
4814
4815                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4816             }
4817             else {
4818                 superclass.setRuntimeAttribute.call(this, attr);
4819             }
4820         };
4821
4822         var translateValues = function(val, start) {
4823             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4824             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4825
4826             return val;
4827         };
4828
4829         var isset = function(prop) {
4830             return (typeof prop !== 'undefined');
4831         };
4832     })();
4833 /*
4834  * Portions of this file are based on pieces of Yahoo User Interface Library
4835  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4836  * YUI licensed under the BSD License:
4837  * http://developer.yahoo.net/yui/license.txt
4838  * <script type="text/javascript">
4839  *
4840  */
4841     (function() {
4842         Roo.lib.Scroll = function(el, attributes, duration, method) {
4843             if (el) {
4844                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4845             }
4846         };
4847
4848         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4849
4850
4851         var Y = Roo.lib;
4852         var superclass = Y.Scroll.superclass;
4853         var proto = Y.Scroll.prototype;
4854
4855         proto.toString = function() {
4856             var el = this.getEl();
4857             var id = el.id || el.tagName;
4858             return ("Scroll " + id);
4859         };
4860
4861         proto.doMethod = function(attr, start, end) {
4862             var val = null;
4863
4864             if (attr == 'scroll') {
4865                 val = [
4866                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4867                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4868                         ];
4869
4870             } else {
4871                 val = superclass.doMethod.call(this, attr, start, end);
4872             }
4873             return val;
4874         };
4875
4876         proto.getAttribute = function(attr) {
4877             var val = null;
4878             var el = this.getEl();
4879
4880             if (attr == 'scroll') {
4881                 val = [ el.scrollLeft, el.scrollTop ];
4882             } else {
4883                 val = superclass.getAttribute.call(this, attr);
4884             }
4885
4886             return val;
4887         };
4888
4889         proto.setAttribute = function(attr, val, unit) {
4890             var el = this.getEl();
4891
4892             if (attr == 'scroll') {
4893                 el.scrollLeft = val[0];
4894                 el.scrollTop = val[1];
4895             } else {
4896                 superclass.setAttribute.call(this, attr, val, unit);
4897             }
4898         };
4899     })();
4900 /**
4901  * Originally based of this code... - refactored for Roo...
4902  * https://github.com/aaalsaleh/undo-manager
4903  
4904  * undo-manager.js
4905  * @author  Abdulrahman Alsaleh 
4906  * @copyright 2015 Abdulrahman Alsaleh 
4907  * @license  MIT License (c) 
4908  *
4909  * Hackily modifyed by alan@roojs.com
4910  *
4911  *
4912  *  
4913  *
4914  *  TOTALLY UNTESTED...
4915  *
4916  *  Documentation to be done....
4917  */
4918  
4919
4920 /**
4921 * @class Roo.lib.UndoManager
4922 * An undo manager implementation in JavaScript. It follows the W3C UndoManager and DOM Transaction
4923 * Draft and the undocumented and disabled Mozilla Firefox's UndoManager implementation.
4924
4925  * Usage:
4926  * <pre><code>
4927
4928
4929 editor.undoManager = new Roo.lib.UndoManager(1000, editor);
4930  
4931 </code></pre>
4932
4933 * For more information see this blog post with examples:
4934 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4935      - Create Elements using DOM, HTML fragments and Templates</a>. 
4936 * @constructor
4937 * @param {Number} limit how far back to go ... use 1000?
4938 * @param {Object} scope usually use document..
4939 */
4940
4941 Roo.lib.UndoManager = function (limit, undoScopeHost)
4942 {
4943     this.stack = [];
4944     this.limit = limit;
4945     this.scope = undoScopeHost;
4946     this.fireEvent = typeof CustomEvent != 'undefined' && undoScopeHost && undoScopeHost.dispatchEvent;
4947     if (this.fireEvent) {
4948         this.bindEvents();
4949     }
4950     this.reset();
4951     
4952 };
4953         
4954 Roo.lib.UndoManager.prototype = {
4955     
4956     limit : false,
4957     stack : false,
4958     scope :  false,
4959     fireEvent : false,
4960     position : 0,
4961     length : 0,
4962     
4963     
4964      /**
4965      * To push and execute a transaction, the method undoManager.transact
4966      * must be called by passing a transaction object as the first argument, and a merge
4967      * flag as the second argument. A transaction object has the following properties:
4968      *
4969      * Usage:
4970 <pre><code>
4971 undoManager.transact({
4972     label: 'Typing',
4973     execute: function() { ... },
4974     undo: function() { ... },
4975     // redo same as execute
4976     redo: function() { this.execute(); }
4977 }, false);
4978
4979 // merge transaction
4980 undoManager.transact({
4981     label: 'Typing',
4982     execute: function() { ... },  // this will be run...
4983     undo: function() { ... }, // what to do when undo is run.
4984     // redo same as execute
4985     redo: function() { this.execute(); }
4986 }, true); 
4987 </code></pre> 
4988      *
4989      * 
4990      * @param {Object} transaction The transaction to add to the stack.
4991      * @return {String} The HTML fragment
4992      */
4993     
4994     
4995     transact : function (transaction, merge)
4996     {
4997         if (arguments.length < 2) {
4998             throw new TypeError('Not enough arguments to UndoManager.transact.');
4999         }
5000
5001         transaction.execute();
5002
5003         this.stack.splice(0, this.position);
5004         if (merge && this.length) {
5005             this.stack[0].push(transaction);
5006         } else {
5007             this.stack.unshift([transaction]);
5008         }
5009     
5010         this.position = 0;
5011
5012         if (this.limit && this.stack.length > this.limit) {
5013             this.length = this.stack.length = this.limit;
5014         } else {
5015             this.length = this.stack.length;
5016         }
5017
5018         if (this.fireEvent) {
5019             this.scope.dispatchEvent(
5020                 new CustomEvent('DOMTransaction', {
5021                     detail: {
5022                         transactions: this.stack[0].slice()
5023                     },
5024                     bubbles: true,
5025                     cancelable: false
5026                 })
5027             );
5028         }
5029         
5030         Roo.log("transaction: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5031       
5032         
5033     },
5034
5035     undo : function ()
5036     {
5037         Roo.log("undo: pos:" + this.position + " len: " + this.length + " slen:" + this.stack.length);
5038         
5039         if (this.position < this.length) {
5040             for (var i = this.stack[this.position].length - 1; i >= 0; i--) {
5041                 this.stack[this.position][i].undo();
5042             }
5043             this.position++;
5044
5045             if (this.fireEvent) {
5046                 this.scope.dispatchEvent(
5047                     new CustomEvent('undo', {
5048                         detail: {
5049                             transactions: this.stack[this.position - 1].slice()
5050                         },
5051                         bubbles: true,
5052                         cancelable: false
5053                     })
5054                 );
5055             }
5056         }
5057     },
5058
5059     redo : function ()
5060     {
5061         if (this.position > 0) {
5062             for (var i = 0, n = this.stack[this.position - 1].length; i < n; i++) {
5063                 this.stack[this.position - 1][i].redo();
5064             }
5065             this.position--;
5066
5067             if (this.fireEvent) {
5068                 this.scope.dispatchEvent(
5069                     new CustomEvent('redo', {
5070                         detail: {
5071                             transactions: this.stack[this.position].slice()
5072                         },
5073                         bubbles: true,
5074                         cancelable: false
5075                     })
5076                 );
5077             }
5078         }
5079     },
5080
5081     item : function (index)
5082     {
5083         if (index >= 0 && index < this.length) {
5084             return this.stack[index].slice();
5085         }
5086         return null;
5087     },
5088
5089     clearUndo : function () {
5090         this.stack.length = this.length = this.position;
5091     },
5092
5093     clearRedo : function () {
5094         this.stack.splice(0, this.position);
5095         this.position = 0;
5096         this.length = this.stack.length;
5097     },
5098     /**
5099      * Reset the undo - probaly done on load to clear all history.
5100      */
5101     reset : function()
5102     {
5103         this.stack = [];
5104         this.position = 0;
5105         this.length = 0;
5106         this.current_html = this.scope.innerHTML;
5107         if (this.timer !== false) {
5108             clearTimeout(this.timer);
5109         }
5110         this.timer = false;
5111         this.merge = false;
5112         this.addEvent();
5113         
5114     },
5115     current_html : '',
5116     timer : false,
5117     merge : false,
5118     
5119     
5120     // this will handle the undo/redo on the element.?
5121     bindEvents : function()
5122     {
5123         var el  = this.scope;
5124         el.undoManager = this;
5125         
5126         
5127         this.scope.addEventListener('keydown', function(e) {
5128             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5129                 if (e.shiftKey) {
5130                     el.undoManager.redo(); // Ctrl/Command + Shift + Z
5131                 } else {
5132                     el.undoManager.undo(); // Ctrl/Command + Z
5133                 }
5134         
5135                 e.preventDefault();
5136             }
5137         });
5138         /// ignore keyup..
5139         this.scope.addEventListener('keyup', function(e) {
5140             if ((e.ctrlKey || e.metaKey) && e.keyCode === 90) {
5141                 e.preventDefault();
5142             }
5143         });
5144         
5145         
5146         
5147         var t = this;
5148         
5149         el.addEventListener('input', function(e) {
5150             if(el.innerHTML == t.current_html) {
5151                 return;
5152             }
5153             // only record events every second.
5154             if (t.timer !== false) {
5155                clearTimeout(t.timer);
5156                t.timer = false;
5157             }
5158             t.timer = setTimeout(function() { t.merge = false; }, 1000);
5159             
5160             t.addEvent(t.merge);
5161             t.merge = true; // ignore changes happening every second..
5162         });
5163         },
5164     /**
5165      * Manually add an event.
5166      * Normall called without arguements - and it will just get added to the stack.
5167      * 
5168      */
5169     
5170     addEvent : function(merge)
5171     {
5172         Roo.log("undomanager +" + (merge ? 'Y':'n'));
5173         // not sure if this should clear the timer 
5174         merge = typeof(merge) == 'undefined' ? false : merge; 
5175         
5176         this.scope.undoManager.transact({
5177             scope : this.scope,
5178             oldHTML: this.current_html,
5179             newHTML: this.scope.innerHTML,
5180             // nothing to execute (content already changed when input is fired)
5181             execute: function() { },
5182             undo: function() {
5183                 this.scope.innerHTML = this.current_html = this.oldHTML;
5184             },
5185             redo: function() {
5186                 this.scope.innerHTML = this.current_html = this.newHTML;
5187             }
5188         }, false); //merge);
5189         
5190         this.merge = merge;
5191         
5192         this.current_html = this.scope.innerHTML;
5193     }
5194     
5195     
5196      
5197     
5198     
5199     
5200 };
5201 /*
5202  * Based on:
5203  * Ext JS Library 1.1.1
5204  * Copyright(c) 2006-2007, Ext JS, LLC.
5205  *
5206  * Originally Released Under LGPL - original licence link has changed is not relivant.
5207  *
5208  * Fork - LGPL
5209  * <script type="text/javascript">
5210  */
5211
5212
5213 // nasty IE9 hack - what a pile of crap that is..
5214
5215  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
5216     Range.prototype.createContextualFragment = function (html) {
5217         var doc = window.document;
5218         var container = doc.createElement("div");
5219         container.innerHTML = html;
5220         var frag = doc.createDocumentFragment(), n;
5221         while ((n = container.firstChild)) {
5222             frag.appendChild(n);
5223         }
5224         return frag;
5225     };
5226 }
5227
5228 /**
5229  * @class Roo.DomHelper
5230  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
5231  * 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>.
5232  * @static
5233  */
5234 Roo.DomHelper = function(){
5235     var tempTableEl = null;
5236     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
5237     var tableRe = /^table|tbody|tr|td$/i;
5238     var xmlns = {};
5239     // build as innerHTML where available
5240     /** @ignore */
5241     var createHtml = function(o){
5242         if(typeof o == 'string'){
5243             return o;
5244         }
5245         var b = "";
5246         if(!o.tag){
5247             o.tag = "div";
5248         }
5249         b += "<" + o.tag;
5250         for(var attr in o){
5251             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
5252             if(attr == "style"){
5253                 var s = o["style"];
5254                 if(typeof s == "function"){
5255                     s = s.call();
5256                 }
5257                 if(typeof s == "string"){
5258                     b += ' style="' + s + '"';
5259                 }else if(typeof s == "object"){
5260                     b += ' style="';
5261                     for(var key in s){
5262                         if(typeof s[key] != "function"){
5263                             b += key + ":" + s[key] + ";";
5264                         }
5265                     }
5266                     b += '"';
5267                 }
5268             }else{
5269                 if(attr == "cls"){
5270                     b += ' class="' + o["cls"] + '"';
5271                 }else if(attr == "htmlFor"){
5272                     b += ' for="' + o["htmlFor"] + '"';
5273                 }else{
5274                     b += " " + attr + '="' + o[attr] + '"';
5275                 }
5276             }
5277         }
5278         if(emptyTags.test(o.tag)){
5279             b += "/>";
5280         }else{
5281             b += ">";
5282             var cn = o.children || o.cn;
5283             if(cn){
5284                 //http://bugs.kde.org/show_bug.cgi?id=71506
5285                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5286                     for(var i = 0, len = cn.length; i < len; i++) {
5287                         b += createHtml(cn[i], b);
5288                     }
5289                 }else{
5290                     b += createHtml(cn, b);
5291                 }
5292             }
5293             if(o.html){
5294                 b += o.html;
5295             }
5296             b += "</" + o.tag + ">";
5297         }
5298         return b;
5299     };
5300
5301     // build as dom
5302     /** @ignore */
5303     var createDom = function(o, parentNode){
5304          
5305         // defininition craeted..
5306         var ns = false;
5307         if (o.ns && o.ns != 'html') {
5308                
5309             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5310                 xmlns[o.ns] = o.xmlns;
5311                 ns = o.xmlns;
5312             }
5313             if (typeof(xmlns[o.ns]) == 'undefined') {
5314                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5315             }
5316             ns = xmlns[o.ns];
5317         }
5318         
5319         
5320         if (typeof(o) == 'string') {
5321             return parentNode.appendChild(document.createTextNode(o));
5322         }
5323         o.tag = o.tag || div;
5324         if (o.ns && Roo.isIE) {
5325             ns = false;
5326             o.tag = o.ns + ':' + o.tag;
5327             
5328         }
5329         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5330         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5331         for(var attr in o){
5332             
5333             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5334                     attr == "style" || typeof o[attr] == "function") { continue; }
5335                     
5336             if(attr=="cls" && Roo.isIE){
5337                 el.className = o["cls"];
5338             }else{
5339                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5340                 else { 
5341                     el[attr] = o[attr];
5342                 }
5343             }
5344         }
5345         Roo.DomHelper.applyStyles(el, o.style);
5346         var cn = o.children || o.cn;
5347         if(cn){
5348             //http://bugs.kde.org/show_bug.cgi?id=71506
5349              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5350                 for(var i = 0, len = cn.length; i < len; i++) {
5351                     createDom(cn[i], el);
5352                 }
5353             }else{
5354                 createDom(cn, el);
5355             }
5356         }
5357         if(o.html){
5358             el.innerHTML = o.html;
5359         }
5360         if(parentNode){
5361            parentNode.appendChild(el);
5362         }
5363         return el;
5364     };
5365
5366     var ieTable = function(depth, s, h, e){
5367         tempTableEl.innerHTML = [s, h, e].join('');
5368         var i = -1, el = tempTableEl;
5369         while(++i < depth && el.firstChild){
5370             el = el.firstChild;
5371         }
5372         return el;
5373     };
5374
5375     // kill repeat to save bytes
5376     var ts = '<table>',
5377         te = '</table>',
5378         tbs = ts+'<tbody>',
5379         tbe = '</tbody>'+te,
5380         trs = tbs + '<tr>',
5381         tre = '</tr>'+tbe;
5382
5383     /**
5384      * @ignore
5385      * Nasty code for IE's broken table implementation
5386      */
5387     var insertIntoTable = function(tag, where, el, html){
5388         if(!tempTableEl){
5389             tempTableEl = document.createElement('div');
5390         }
5391         var node;
5392         var before = null;
5393         if(tag == 'td'){
5394             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5395                 return;
5396             }
5397             if(where == 'beforebegin'){
5398                 before = el;
5399                 el = el.parentNode;
5400             } else{
5401                 before = el.nextSibling;
5402                 el = el.parentNode;
5403             }
5404             node = ieTable(4, trs, html, tre);
5405         }
5406         else if(tag == 'tr'){
5407             if(where == 'beforebegin'){
5408                 before = el;
5409                 el = el.parentNode;
5410                 node = ieTable(3, tbs, html, tbe);
5411             } else if(where == 'afterend'){
5412                 before = el.nextSibling;
5413                 el = el.parentNode;
5414                 node = ieTable(3, tbs, html, tbe);
5415             } else{ // INTO a TR
5416                 if(where == 'afterbegin'){
5417                     before = el.firstChild;
5418                 }
5419                 node = ieTable(4, trs, html, tre);
5420             }
5421         } else if(tag == 'tbody'){
5422             if(where == 'beforebegin'){
5423                 before = el;
5424                 el = el.parentNode;
5425                 node = ieTable(2, ts, html, te);
5426             } else if(where == 'afterend'){
5427                 before = el.nextSibling;
5428                 el = el.parentNode;
5429                 node = ieTable(2, ts, html, te);
5430             } else{
5431                 if(where == 'afterbegin'){
5432                     before = el.firstChild;
5433                 }
5434                 node = ieTable(3, tbs, html, tbe);
5435             }
5436         } else{ // TABLE
5437             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5438                 return;
5439             }
5440             if(where == 'afterbegin'){
5441                 before = el.firstChild;
5442             }
5443             node = ieTable(2, ts, html, te);
5444         }
5445         el.insertBefore(node, before);
5446         return node;
5447     };
5448     
5449     // this is a bit like the react update code...
5450     // 
5451     
5452     var updateNode = function(from, to)
5453     {
5454         // should we handle non-standard elements?
5455         Roo.log(["UpdateNode" , from, to]);
5456         if (from.nodeType != to.nodeType) {
5457             Roo.log(["ReplaceChild - mismatch notType" , to, from ]);
5458             from.parentNode.replaceChild(to, from);
5459         }
5460         
5461         if (from.nodeType == 3) {
5462             // assume it's text?!
5463             if (from.data == to.data) {
5464                 return;
5465             }
5466             from.data = to.data;
5467             return;
5468         }
5469         
5470         // assume 'to' doesnt have '1/3 nodetypes!
5471         if (from.nodeType !=1 || from.tagName != to.tagName) {
5472             Roo.log(["ReplaceChild" , from, to ]);
5473             from.parentNode.replaceChild(to, from);
5474             return;
5475         }
5476         // compare attributes
5477         var ar = Array.from(from.attributes);
5478         for(var i = 0; i< ar.length;i++) {
5479             if (to.hasAttribute(ar[i].name)) {
5480                 continue;
5481             }
5482             if (ar[i].name == 'id') { // always keep ids?
5483                 continue;
5484             }
5485             from.removeAttribute(ar[i].name);
5486         }
5487         ar = to.attributes;
5488         for(var i = 0; i< ar.length;i++) {
5489             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5490                 continue;
5491             }
5492             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5493         }
5494         // children
5495         var far = Array.from(from.childNodes);
5496         var tar = Array.from(to.childNodes);
5497         // if the lengths are different.. then it's probably a editable content change, rather than
5498         // a change of the block definition..
5499         
5500         // this did notwork , as our rebuilt nodes did not include ID's so did not match at all.
5501          /*if (from.innerHTML == to.innerHTML) {
5502             return;
5503         }
5504         if (far.length != tar.length) {
5505             from.innerHTML = to.innerHTML;
5506             return;
5507         }
5508         */
5509         
5510         for(var i = 0; i < Math.max(tar.length, far.length); i++) {
5511             if (i >= far.length) {
5512                 from.appendChild(tar[i]);
5513                 Roo.log(["add", tar[i]]);
5514                 
5515             } else if ( i  >= tar.length) {
5516                 from.removeChild(far[i]);
5517                 Roo.log(["remove", far[i]]);
5518             } else {
5519                 
5520                 updateNode(far[i], tar[i]);
5521             }    
5522         }
5523         
5524         
5525         
5526         
5527     };
5528     
5529     
5530
5531     return {
5532         /** True to force the use of DOM instead of html fragments @type Boolean */
5533         useDom : false,
5534     
5535         /**
5536          * Returns the markup for the passed Element(s) config
5537          * @param {Object} o The Dom object spec (and children)
5538          * @return {String}
5539          */
5540         markup : function(o){
5541             return createHtml(o);
5542         },
5543     
5544         /**
5545          * Applies a style specification to an element
5546          * @param {String/HTMLElement} el The element to apply styles to
5547          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5548          * a function which returns such a specification.
5549          */
5550         applyStyles : function(el, styles){
5551             if(styles){
5552                el = Roo.fly(el);
5553                if(typeof styles == "string"){
5554                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5555                    var matches;
5556                    while ((matches = re.exec(styles)) != null){
5557                        el.setStyle(matches[1], matches[2]);
5558                    }
5559                }else if (typeof styles == "object"){
5560                    for (var style in styles){
5561                       el.setStyle(style, styles[style]);
5562                    }
5563                }else if (typeof styles == "function"){
5564                     Roo.DomHelper.applyStyles(el, styles.call());
5565                }
5566             }
5567         },
5568     
5569         /**
5570          * Inserts an HTML fragment into the Dom
5571          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5572          * @param {HTMLElement} el The context element
5573          * @param {String} html The HTML fragmenet
5574          * @return {HTMLElement} The new node
5575          */
5576         insertHtml : function(where, el, html){
5577             where = where.toLowerCase();
5578             if(el.insertAdjacentHTML){
5579                 if(tableRe.test(el.tagName)){
5580                     var rs;
5581                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5582                         return rs;
5583                     }
5584                 }
5585                 switch(where){
5586                     case "beforebegin":
5587                         el.insertAdjacentHTML('BeforeBegin', html);
5588                         return el.previousSibling;
5589                     case "afterbegin":
5590                         el.insertAdjacentHTML('AfterBegin', html);
5591                         return el.firstChild;
5592                     case "beforeend":
5593                         el.insertAdjacentHTML('BeforeEnd', html);
5594                         return el.lastChild;
5595                     case "afterend":
5596                         el.insertAdjacentHTML('AfterEnd', html);
5597                         return el.nextSibling;
5598                 }
5599                 throw 'Illegal insertion point -> "' + where + '"';
5600             }
5601             var range = el.ownerDocument.createRange();
5602             var frag;
5603             switch(where){
5604                  case "beforebegin":
5605                     range.setStartBefore(el);
5606                     frag = range.createContextualFragment(html);
5607                     el.parentNode.insertBefore(frag, el);
5608                     return el.previousSibling;
5609                  case "afterbegin":
5610                     if(el.firstChild){
5611                         range.setStartBefore(el.firstChild);
5612                         frag = range.createContextualFragment(html);
5613                         el.insertBefore(frag, el.firstChild);
5614                         return el.firstChild;
5615                     }else{
5616                         el.innerHTML = html;
5617                         return el.firstChild;
5618                     }
5619                 case "beforeend":
5620                     if(el.lastChild){
5621                         range.setStartAfter(el.lastChild);
5622                         frag = range.createContextualFragment(html);
5623                         el.appendChild(frag);
5624                         return el.lastChild;
5625                     }else{
5626                         el.innerHTML = html;
5627                         return el.lastChild;
5628                     }
5629                 case "afterend":
5630                     range.setStartAfter(el);
5631                     frag = range.createContextualFragment(html);
5632                     el.parentNode.insertBefore(frag, el.nextSibling);
5633                     return el.nextSibling;
5634                 }
5635                 throw 'Illegal insertion point -> "' + where + '"';
5636         },
5637     
5638         /**
5639          * Creates new Dom element(s) and inserts them before el
5640          * @param {String/HTMLElement/Element} el The context element
5641          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5642          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5643          * @return {HTMLElement/Roo.Element} The new node
5644          */
5645         insertBefore : function(el, o, returnElement){
5646             return this.doInsert(el, o, returnElement, "beforeBegin");
5647         },
5648     
5649         /**
5650          * Creates new Dom element(s) and inserts them after el
5651          * @param {String/HTMLElement/Element} el The context element
5652          * @param {Object} o The Dom object spec (and children)
5653          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5654          * @return {HTMLElement/Roo.Element} The new node
5655          */
5656         insertAfter : function(el, o, returnElement){
5657             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5658         },
5659     
5660         /**
5661          * Creates new Dom element(s) and inserts them as the first child of el
5662          * @param {String/HTMLElement/Element} el The context element
5663          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5664          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5665          * @return {HTMLElement/Roo.Element} The new node
5666          */
5667         insertFirst : function(el, o, returnElement){
5668             return this.doInsert(el, o, returnElement, "afterBegin");
5669         },
5670     
5671         // private
5672         doInsert : function(el, o, returnElement, pos, sibling){
5673             el = Roo.getDom(el);
5674             var newNode;
5675             if(this.useDom || o.ns){
5676                 newNode = createDom(o, null);
5677                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5678             }else{
5679                 var html = createHtml(o);
5680                 newNode = this.insertHtml(pos, el, html);
5681             }
5682             return returnElement ? Roo.get(newNode, true) : newNode;
5683         },
5684     
5685         /**
5686          * Creates new Dom element(s) and appends them to el
5687          * @param {String/HTMLElement/Element} el The context element
5688          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5689          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5690          * @return {HTMLElement/Roo.Element} The new node
5691          */
5692         append : function(el, o, returnElement){
5693             el = Roo.getDom(el);
5694             var newNode;
5695             if(this.useDom || o.ns){
5696                 newNode = createDom(o, null);
5697                 el.appendChild(newNode);
5698             }else{
5699                 var html = createHtml(o);
5700                 newNode = this.insertHtml("beforeEnd", el, html);
5701             }
5702             return returnElement ? Roo.get(newNode, true) : newNode;
5703         },
5704     
5705         /**
5706          * Creates new Dom element(s) and overwrites the contents of el with them
5707          * @param {String/HTMLElement/Element} el The context element
5708          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5709          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5710          * @return {HTMLElement/Roo.Element} The new node
5711          */
5712         overwrite : function(el, o, returnElement)
5713         {
5714             el = Roo.getDom(el);
5715             if (o.ns) {
5716               
5717                 while (el.childNodes.length) {
5718                     el.removeChild(el.firstChild);
5719                 }
5720                 createDom(o, el);
5721             } else {
5722                 el.innerHTML = createHtml(o);   
5723             }
5724             
5725             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5726         },
5727     
5728         /**
5729          * Creates a new Roo.DomHelper.Template from the Dom object spec
5730          * @param {Object} o The Dom object spec (and children)
5731          * @return {Roo.DomHelper.Template} The new template
5732          */
5733         createTemplate : function(o){
5734             var html = createHtml(o);
5735             return new Roo.Template(html);
5736         },
5737          /**
5738          * Updates the first element with the spec from the o (replacing if necessary)
5739          * This iterates through the children, and updates attributes / children etc..
5740          * @param {String/HTMLElement/Element} el The context element
5741          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5742          */
5743         
5744         update : function(el, o)
5745         {
5746             updateNode(Roo.getDom(el), createDom(o));
5747             
5748         }
5749         
5750         
5751     };
5752 }();
5753 /*
5754  * Based on:
5755  * Ext JS Library 1.1.1
5756  * Copyright(c) 2006-2007, Ext JS, LLC.
5757  *
5758  * Originally Released Under LGPL - original licence link has changed is not relivant.
5759  *
5760  * Fork - LGPL
5761  * <script type="text/javascript">
5762  */
5763  
5764 /**
5765 * @class Roo.Template
5766 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5767 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5768 * Usage:
5769 <pre><code>
5770 var t = new Roo.Template({
5771     html :  '&lt;div name="{id}"&gt;' + 
5772         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5773         '&lt;/div&gt;',
5774     myformat: function (value, allValues) {
5775         return 'XX' + value;
5776     }
5777 });
5778 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5779 </code></pre>
5780 * For more information see this blog post with examples:
5781 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5782      - Create Elements using DOM, HTML fragments and Templates</a>. 
5783 * @constructor
5784 * @param {Object} cfg - Configuration object.
5785 */
5786 Roo.Template = function(cfg){
5787     // BC!
5788     if(cfg instanceof Array){
5789         cfg = cfg.join("");
5790     }else if(arguments.length > 1){
5791         cfg = Array.prototype.join.call(arguments, "");
5792     }
5793     
5794     
5795     if (typeof(cfg) == 'object') {
5796         Roo.apply(this,cfg)
5797     } else {
5798         // bc
5799         this.html = cfg;
5800     }
5801     if (this.url) {
5802         this.load();
5803     }
5804     
5805 };
5806 Roo.Template.prototype = {
5807     
5808     /**
5809      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5810      */
5811     onLoad : false,
5812     
5813     
5814     /**
5815      * @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..
5816      *                    it should be fixed so that template is observable...
5817      */
5818     url : false,
5819     /**
5820      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5821      */
5822     html : '',
5823     
5824     
5825     compiled : false,
5826     loaded : false,
5827     /**
5828      * Returns an HTML fragment of this template with the specified values applied.
5829      * @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'})
5830      * @return {String} The HTML fragment
5831      */
5832     
5833    
5834     
5835     applyTemplate : function(values){
5836         //Roo.log(["applyTemplate", values]);
5837         try {
5838            
5839             if(this.compiled){
5840                 return this.compiled(values);
5841             }
5842             var useF = this.disableFormats !== true;
5843             var fm = Roo.util.Format, tpl = this;
5844             var fn = function(m, name, format, args){
5845                 if(format && useF){
5846                     if(format.substr(0, 5) == "this."){
5847                         return tpl.call(format.substr(5), values[name], values);
5848                     }else{
5849                         if(args){
5850                             // quoted values are required for strings in compiled templates, 
5851                             // but for non compiled we need to strip them
5852                             // quoted reversed for jsmin
5853                             var re = /^\s*['"](.*)["']\s*$/;
5854                             args = args.split(',');
5855                             for(var i = 0, len = args.length; i < len; i++){
5856                                 args[i] = args[i].replace(re, "$1");
5857                             }
5858                             args = [values[name]].concat(args);
5859                         }else{
5860                             args = [values[name]];
5861                         }
5862                         return fm[format].apply(fm, args);
5863                     }
5864                 }else{
5865                     return values[name] !== undefined ? values[name] : "";
5866                 }
5867             };
5868             return this.html.replace(this.re, fn);
5869         } catch (e) {
5870             Roo.log(e);
5871             throw e;
5872         }
5873          
5874     },
5875     
5876     loading : false,
5877       
5878     load : function ()
5879     {
5880          
5881         if (this.loading) {
5882             return;
5883         }
5884         var _t = this;
5885         
5886         this.loading = true;
5887         this.compiled = false;
5888         
5889         var cx = new Roo.data.Connection();
5890         cx.request({
5891             url : this.url,
5892             method : 'GET',
5893             success : function (response) {
5894                 _t.loading = false;
5895                 _t.url = false;
5896                 
5897                 _t.set(response.responseText,true);
5898                 _t.loaded = true;
5899                 if (_t.onLoad) {
5900                     _t.onLoad();
5901                 }
5902              },
5903             failure : function(response) {
5904                 Roo.log("Template failed to load from " + _t.url);
5905                 _t.loading = false;
5906             }
5907         });
5908     },
5909
5910     /**
5911      * Sets the HTML used as the template and optionally compiles it.
5912      * @param {String} html
5913      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5914      * @return {Roo.Template} this
5915      */
5916     set : function(html, compile){
5917         this.html = html;
5918         this.compiled = false;
5919         if(compile){
5920             this.compile();
5921         }
5922         return this;
5923     },
5924     
5925     /**
5926      * True to disable format functions (defaults to false)
5927      * @type Boolean
5928      */
5929     disableFormats : false,
5930     
5931     /**
5932     * The regular expression used to match template variables 
5933     * @type RegExp
5934     * @property 
5935     */
5936     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5937     
5938     /**
5939      * Compiles the template into an internal function, eliminating the RegEx overhead.
5940      * @return {Roo.Template} this
5941      */
5942     compile : function(){
5943         var fm = Roo.util.Format;
5944         var useF = this.disableFormats !== true;
5945         var sep = Roo.isGecko ? "+" : ",";
5946         var fn = function(m, name, format, args){
5947             if(format && useF){
5948                 args = args ? ',' + args : "";
5949                 if(format.substr(0, 5) != "this."){
5950                     format = "fm." + format + '(';
5951                 }else{
5952                     format = 'this.call("'+ format.substr(5) + '", ';
5953                     args = ", values";
5954                 }
5955             }else{
5956                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5957             }
5958             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5959         };
5960         var body;
5961         // branched to use + in gecko and [].join() in others
5962         if(Roo.isGecko){
5963             body = "this.compiled = function(values){ return '" +
5964                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5965                     "';};";
5966         }else{
5967             body = ["this.compiled = function(values){ return ['"];
5968             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5969             body.push("'].join('');};");
5970             body = body.join('');
5971         }
5972         /**
5973          * eval:var:values
5974          * eval:var:fm
5975          */
5976         eval(body);
5977         return this;
5978     },
5979     
5980     // private function used to call members
5981     call : function(fnName, value, allValues){
5982         return this[fnName](value, allValues);
5983     },
5984     
5985     /**
5986      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5987      * @param {String/HTMLElement/Roo.Element} el The context element
5988      * @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'})
5989      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5990      * @return {HTMLElement/Roo.Element} The new node or Element
5991      */
5992     insertFirst: function(el, values, returnElement){
5993         return this.doInsert('afterBegin', el, values, returnElement);
5994     },
5995
5996     /**
5997      * Applies the supplied values to the template and inserts the new node(s) before el.
5998      * @param {String/HTMLElement/Roo.Element} el The context element
5999      * @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'})
6000      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6001      * @return {HTMLElement/Roo.Element} The new node or Element
6002      */
6003     insertBefore: function(el, values, returnElement){
6004         return this.doInsert('beforeBegin', el, values, returnElement);
6005     },
6006
6007     /**
6008      * Applies the supplied values to the template and inserts the new node(s) after el.
6009      * @param {String/HTMLElement/Roo.Element} el The context element
6010      * @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'})
6011      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6012      * @return {HTMLElement/Roo.Element} The new node or Element
6013      */
6014     insertAfter : function(el, values, returnElement){
6015         return this.doInsert('afterEnd', el, values, returnElement);
6016     },
6017     
6018     /**
6019      * Applies the supplied values to the template and appends the new node(s) to el.
6020      * @param {String/HTMLElement/Roo.Element} el The context element
6021      * @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'})
6022      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6023      * @return {HTMLElement/Roo.Element} The new node or Element
6024      */
6025     append : function(el, values, returnElement){
6026         return this.doInsert('beforeEnd', el, values, returnElement);
6027     },
6028
6029     doInsert : function(where, el, values, returnEl){
6030         el = Roo.getDom(el);
6031         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
6032         return returnEl ? Roo.get(newNode, true) : newNode;
6033     },
6034
6035     /**
6036      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
6037      * @param {String/HTMLElement/Roo.Element} el The context element
6038      * @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'})
6039      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
6040      * @return {HTMLElement/Roo.Element} The new node or Element
6041      */
6042     overwrite : function(el, values, returnElement){
6043         el = Roo.getDom(el);
6044         el.innerHTML = this.applyTemplate(values);
6045         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
6046     }
6047 };
6048 /**
6049  * Alias for {@link #applyTemplate}
6050  * @method
6051  */
6052 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
6053
6054 // backwards compat
6055 Roo.DomHelper.Template = Roo.Template;
6056
6057 /**
6058  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
6059  * @param {String/HTMLElement} el A DOM element or its id
6060  * @returns {Roo.Template} The created template
6061  * @static
6062  */
6063 Roo.Template.from = function(el){
6064     el = Roo.getDom(el);
6065     return new Roo.Template(el.value || el.innerHTML);
6066 };/*
6067  * Based on:
6068  * Ext JS Library 1.1.1
6069  * Copyright(c) 2006-2007, Ext JS, LLC.
6070  *
6071  * Originally Released Under LGPL - original licence link has changed is not relivant.
6072  *
6073  * Fork - LGPL
6074  * <script type="text/javascript">
6075  */
6076  
6077
6078 /*
6079  * This is code is also distributed under MIT license for use
6080  * with jQuery and prototype JavaScript libraries.
6081  */
6082 /**
6083  * @class Roo.DomQuery
6084 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).
6085 <p>
6086 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>
6087
6088 <p>
6089 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.
6090 </p>
6091 <h4>Element Selectors:</h4>
6092 <ul class="list">
6093     <li> <b>*</b> any element</li>
6094     <li> <b>E</b> an element with the tag E</li>
6095     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
6096     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
6097     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
6098     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
6099 </ul>
6100 <h4>Attribute Selectors:</h4>
6101 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
6102 <ul class="list">
6103     <li> <b>E[foo]</b> has an attribute "foo"</li>
6104     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
6105     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
6106     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
6107     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
6108     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
6109     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
6110 </ul>
6111 <h4>Pseudo Classes:</h4>
6112 <ul class="list">
6113     <li> <b>E:first-child</b> E is the first child of its parent</li>
6114     <li> <b>E:last-child</b> E is the last child of its parent</li>
6115     <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>
6116     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
6117     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
6118     <li> <b>E:only-child</b> E is the only child of its parent</li>
6119     <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>
6120     <li> <b>E:first</b> the first E in the resultset</li>
6121     <li> <b>E:last</b> the last E in the resultset</li>
6122     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
6123     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
6124     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
6125     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
6126     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
6127     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
6128     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
6129     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
6130     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
6131 </ul>
6132 <h4>CSS Value Selectors:</h4>
6133 <ul class="list">
6134     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
6135     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
6136     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
6137     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
6138     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
6139     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
6140 </ul>
6141  * @static
6142  */
6143 Roo.DomQuery = function(){
6144     var cache = {}, simpleCache = {}, valueCache = {};
6145     var nonSpace = /\S/;
6146     var trimRe = /^\s+|\s+$/g;
6147     var tplRe = /\{(\d+)\}/g;
6148     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
6149     var tagTokenRe = /^(#)?([\w-\*]+)/;
6150     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
6151
6152     function child(p, index){
6153         var i = 0;
6154         var n = p.firstChild;
6155         while(n){
6156             if(n.nodeType == 1){
6157                if(++i == index){
6158                    return n;
6159                }
6160             }
6161             n = n.nextSibling;
6162         }
6163         return null;
6164     };
6165
6166     function next(n){
6167         while((n = n.nextSibling) && n.nodeType != 1);
6168         return n;
6169     };
6170
6171     function prev(n){
6172         while((n = n.previousSibling) && n.nodeType != 1);
6173         return n;
6174     };
6175
6176     function children(d){
6177         var n = d.firstChild, ni = -1;
6178             while(n){
6179                 var nx = n.nextSibling;
6180                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
6181                     d.removeChild(n);
6182                 }else{
6183                     n.nodeIndex = ++ni;
6184                 }
6185                 n = nx;
6186             }
6187             return this;
6188         };
6189
6190     function byClassName(c, a, v){
6191         if(!v){
6192             return c;
6193         }
6194         var r = [], ri = -1, cn;
6195         for(var i = 0, ci; ci = c[i]; i++){
6196             
6197             
6198             if((' '+
6199                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
6200                  +' ').indexOf(v) != -1){
6201                 r[++ri] = ci;
6202             }
6203         }
6204         return r;
6205     };
6206
6207     function attrValue(n, attr){
6208         if(!n.tagName && typeof n.length != "undefined"){
6209             n = n[0];
6210         }
6211         if(!n){
6212             return null;
6213         }
6214         if(attr == "for"){
6215             return n.htmlFor;
6216         }
6217         if(attr == "class" || attr == "className"){
6218             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
6219         }
6220         return n.getAttribute(attr) || n[attr];
6221
6222     };
6223
6224     function getNodes(ns, mode, tagName){
6225         var result = [], ri = -1, cs;
6226         if(!ns){
6227             return result;
6228         }
6229         tagName = tagName || "*";
6230         if(typeof ns.getElementsByTagName != "undefined"){
6231             ns = [ns];
6232         }
6233         if(!mode){
6234             for(var i = 0, ni; ni = ns[i]; i++){
6235                 cs = ni.getElementsByTagName(tagName);
6236                 for(var j = 0, ci; ci = cs[j]; j++){
6237                     result[++ri] = ci;
6238                 }
6239             }
6240         }else if(mode == "/" || mode == ">"){
6241             var utag = tagName.toUpperCase();
6242             for(var i = 0, ni, cn; ni = ns[i]; i++){
6243                 cn = ni.children || ni.childNodes;
6244                 for(var j = 0, cj; cj = cn[j]; j++){
6245                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
6246                         result[++ri] = cj;
6247                     }
6248                 }
6249             }
6250         }else if(mode == "+"){
6251             var utag = tagName.toUpperCase();
6252             for(var i = 0, n; n = ns[i]; i++){
6253                 while((n = n.nextSibling) && n.nodeType != 1);
6254                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
6255                     result[++ri] = n;
6256                 }
6257             }
6258         }else if(mode == "~"){
6259             for(var i = 0, n; n = ns[i]; i++){
6260                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
6261                 if(n){
6262                     result[++ri] = n;
6263                 }
6264             }
6265         }
6266         return result;
6267     };
6268
6269     function concat(a, b){
6270         if(b.slice){
6271             return a.concat(b);
6272         }
6273         for(var i = 0, l = b.length; i < l; i++){
6274             a[a.length] = b[i];
6275         }
6276         return a;
6277     }
6278
6279     function byTag(cs, tagName){
6280         if(cs.tagName || cs == document){
6281             cs = [cs];
6282         }
6283         if(!tagName){
6284             return cs;
6285         }
6286         var r = [], ri = -1;
6287         tagName = tagName.toLowerCase();
6288         for(var i = 0, ci; ci = cs[i]; i++){
6289             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
6290                 r[++ri] = ci;
6291             }
6292         }
6293         return r;
6294     };
6295
6296     function byId(cs, attr, id){
6297         if(cs.tagName || cs == document){
6298             cs = [cs];
6299         }
6300         if(!id){
6301             return cs;
6302         }
6303         var r = [], ri = -1;
6304         for(var i = 0,ci; ci = cs[i]; i++){
6305             if(ci && ci.id == id){
6306                 r[++ri] = ci;
6307                 return r;
6308             }
6309         }
6310         return r;
6311     };
6312
6313     function byAttribute(cs, attr, value, op, custom){
6314         var r = [], ri = -1, st = custom=="{";
6315         var f = Roo.DomQuery.operators[op];
6316         for(var i = 0, ci; ci = cs[i]; i++){
6317             var a;
6318             if(st){
6319                 a = Roo.DomQuery.getStyle(ci, attr);
6320             }
6321             else if(attr == "class" || attr == "className"){
6322                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6323             }else if(attr == "for"){
6324                 a = ci.htmlFor;
6325             }else if(attr == "href"){
6326                 a = ci.getAttribute("href", 2);
6327             }else{
6328                 a = ci.getAttribute(attr);
6329             }
6330             if((f && f(a, value)) || (!f && a)){
6331                 r[++ri] = ci;
6332             }
6333         }
6334         return r;
6335     };
6336
6337     function byPseudo(cs, name, value){
6338         return Roo.DomQuery.pseudos[name](cs, value);
6339     };
6340
6341     // This is for IE MSXML which does not support expandos.
6342     // IE runs the same speed using setAttribute, however FF slows way down
6343     // and Safari completely fails so they need to continue to use expandos.
6344     var isIE = window.ActiveXObject ? true : false;
6345
6346     // this eval is stop the compressor from
6347     // renaming the variable to something shorter
6348     
6349     /** eval:var:batch */
6350     var batch = 30803; 
6351
6352     var key = 30803;
6353
6354     function nodupIEXml(cs){
6355         var d = ++key;
6356         cs[0].setAttribute("_nodup", d);
6357         var r = [cs[0]];
6358         for(var i = 1, len = cs.length; i < len; i++){
6359             var c = cs[i];
6360             if(!c.getAttribute("_nodup") != d){
6361                 c.setAttribute("_nodup", d);
6362                 r[r.length] = c;
6363             }
6364         }
6365         for(var i = 0, len = cs.length; i < len; i++){
6366             cs[i].removeAttribute("_nodup");
6367         }
6368         return r;
6369     }
6370
6371     function nodup(cs){
6372         if(!cs){
6373             return [];
6374         }
6375         var len = cs.length, c, i, r = cs, cj, ri = -1;
6376         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6377             return cs;
6378         }
6379         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6380             return nodupIEXml(cs);
6381         }
6382         var d = ++key;
6383         cs[0]._nodup = d;
6384         for(i = 1; c = cs[i]; i++){
6385             if(c._nodup != d){
6386                 c._nodup = d;
6387             }else{
6388                 r = [];
6389                 for(var j = 0; j < i; j++){
6390                     r[++ri] = cs[j];
6391                 }
6392                 for(j = i+1; cj = cs[j]; j++){
6393                     if(cj._nodup != d){
6394                         cj._nodup = d;
6395                         r[++ri] = cj;
6396                     }
6397                 }
6398                 return r;
6399             }
6400         }
6401         return r;
6402     }
6403
6404     function quickDiffIEXml(c1, c2){
6405         var d = ++key;
6406         for(var i = 0, len = c1.length; i < len; i++){
6407             c1[i].setAttribute("_qdiff", d);
6408         }
6409         var r = [];
6410         for(var i = 0, len = c2.length; i < len; i++){
6411             if(c2[i].getAttribute("_qdiff") != d){
6412                 r[r.length] = c2[i];
6413             }
6414         }
6415         for(var i = 0, len = c1.length; i < len; i++){
6416            c1[i].removeAttribute("_qdiff");
6417         }
6418         return r;
6419     }
6420
6421     function quickDiff(c1, c2){
6422         var len1 = c1.length;
6423         if(!len1){
6424             return c2;
6425         }
6426         if(isIE && c1[0].selectSingleNode){
6427             return quickDiffIEXml(c1, c2);
6428         }
6429         var d = ++key;
6430         for(var i = 0; i < len1; i++){
6431             c1[i]._qdiff = d;
6432         }
6433         var r = [];
6434         for(var i = 0, len = c2.length; i < len; i++){
6435             if(c2[i]._qdiff != d){
6436                 r[r.length] = c2[i];
6437             }
6438         }
6439         return r;
6440     }
6441
6442     function quickId(ns, mode, root, id){
6443         if(ns == root){
6444            var d = root.ownerDocument || root;
6445            return d.getElementById(id);
6446         }
6447         ns = getNodes(ns, mode, "*");
6448         return byId(ns, null, id);
6449     }
6450
6451     return {
6452         getStyle : function(el, name){
6453             return Roo.fly(el).getStyle(name);
6454         },
6455         /**
6456          * Compiles a selector/xpath query into a reusable function. The returned function
6457          * takes one parameter "root" (optional), which is the context node from where the query should start.
6458          * @param {String} selector The selector/xpath query
6459          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6460          * @return {Function}
6461          */
6462         compile : function(path, type){
6463             type = type || "select";
6464             
6465             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6466             var q = path, mode, lq;
6467             var tk = Roo.DomQuery.matchers;
6468             var tklen = tk.length;
6469             var mm;
6470
6471             // accept leading mode switch
6472             var lmode = q.match(modeRe);
6473             if(lmode && lmode[1]){
6474                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6475                 q = q.replace(lmode[1], "");
6476             }
6477             // strip leading slashes
6478             while(path.substr(0, 1)=="/"){
6479                 path = path.substr(1);
6480             }
6481
6482             while(q && lq != q){
6483                 lq = q;
6484                 var tm = q.match(tagTokenRe);
6485                 if(type == "select"){
6486                     if(tm){
6487                         if(tm[1] == "#"){
6488                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6489                         }else{
6490                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6491                         }
6492                         q = q.replace(tm[0], "");
6493                     }else if(q.substr(0, 1) != '@'){
6494                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6495                     }
6496                 }else{
6497                     if(tm){
6498                         if(tm[1] == "#"){
6499                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6500                         }else{
6501                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6502                         }
6503                         q = q.replace(tm[0], "");
6504                     }
6505                 }
6506                 while(!(mm = q.match(modeRe))){
6507                     var matched = false;
6508                     for(var j = 0; j < tklen; j++){
6509                         var t = tk[j];
6510                         var m = q.match(t.re);
6511                         if(m){
6512                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6513                                                     return m[i];
6514                                                 });
6515                             q = q.replace(m[0], "");
6516                             matched = true;
6517                             break;
6518                         }
6519                     }
6520                     // prevent infinite loop on bad selector
6521                     if(!matched){
6522                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6523                     }
6524                 }
6525                 if(mm[1]){
6526                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6527                     q = q.replace(mm[1], "");
6528                 }
6529             }
6530             fn[fn.length] = "return nodup(n);\n}";
6531             
6532              /** 
6533               * list of variables that need from compression as they are used by eval.
6534              *  eval:var:batch 
6535              *  eval:var:nodup
6536              *  eval:var:byTag
6537              *  eval:var:ById
6538              *  eval:var:getNodes
6539              *  eval:var:quickId
6540              *  eval:var:mode
6541              *  eval:var:root
6542              *  eval:var:n
6543              *  eval:var:byClassName
6544              *  eval:var:byPseudo
6545              *  eval:var:byAttribute
6546              *  eval:var:attrValue
6547              * 
6548              **/ 
6549             eval(fn.join(""));
6550             return f;
6551         },
6552
6553         /**
6554          * Selects a group of elements.
6555          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6556          * @param {Node} root (optional) The start of the query (defaults to document).
6557          * @return {Array}
6558          */
6559         select : function(path, root, type){
6560             if(!root || root == document){
6561                 root = document;
6562             }
6563             if(typeof root == "string"){
6564                 root = document.getElementById(root);
6565             }
6566             var paths = path.split(",");
6567             var results = [];
6568             for(var i = 0, len = paths.length; i < len; i++){
6569                 var p = paths[i].replace(trimRe, "");
6570                 if(!cache[p]){
6571                     cache[p] = Roo.DomQuery.compile(p);
6572                     if(!cache[p]){
6573                         throw p + " is not a valid selector";
6574                     }
6575                 }
6576                 var result = cache[p](root);
6577                 if(result && result != document){
6578                     results = results.concat(result);
6579                 }
6580             }
6581             if(paths.length > 1){
6582                 return nodup(results);
6583             }
6584             return results;
6585         },
6586
6587         /**
6588          * Selects a single element.
6589          * @param {String} selector The selector/xpath query
6590          * @param {Node} root (optional) The start of the query (defaults to document).
6591          * @return {Element}
6592          */
6593         selectNode : function(path, root){
6594             return Roo.DomQuery.select(path, root)[0];
6595         },
6596
6597         /**
6598          * Selects the value of a node, optionally replacing null with the defaultValue.
6599          * @param {String} selector The selector/xpath query
6600          * @param {Node} root (optional) The start of the query (defaults to document).
6601          * @param {String} defaultValue
6602          */
6603         selectValue : function(path, root, defaultValue){
6604             path = path.replace(trimRe, "");
6605             if(!valueCache[path]){
6606                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6607             }
6608             var n = valueCache[path](root);
6609             n = n[0] ? n[0] : n;
6610             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6611             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6612         },
6613
6614         /**
6615          * Selects the value of a node, parsing integers and floats.
6616          * @param {String} selector The selector/xpath query
6617          * @param {Node} root (optional) The start of the query (defaults to document).
6618          * @param {Number} defaultValue
6619          * @return {Number}
6620          */
6621         selectNumber : function(path, root, defaultValue){
6622             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6623             return parseFloat(v);
6624         },
6625
6626         /**
6627          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6628          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6629          * @param {String} selector The simple selector to test
6630          * @return {Boolean}
6631          */
6632         is : function(el, ss){
6633             if(typeof el == "string"){
6634                 el = document.getElementById(el);
6635             }
6636             var isArray = (el instanceof Array);
6637             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6638             return isArray ? (result.length == el.length) : (result.length > 0);
6639         },
6640
6641         /**
6642          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6643          * @param {Array} el An array of elements to filter
6644          * @param {String} selector The simple selector to test
6645          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6646          * the selector instead of the ones that match
6647          * @return {Array}
6648          */
6649         filter : function(els, ss, nonMatches){
6650             ss = ss.replace(trimRe, "");
6651             if(!simpleCache[ss]){
6652                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6653             }
6654             var result = simpleCache[ss](els);
6655             return nonMatches ? quickDiff(result, els) : result;
6656         },
6657
6658         /**
6659          * Collection of matching regular expressions and code snippets.
6660          */
6661         matchers : [{
6662                 re: /^\.([\w-]+)/,
6663                 select: 'n = byClassName(n, null, " {1} ");'
6664             }, {
6665                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6666                 select: 'n = byPseudo(n, "{1}", "{2}");'
6667             },{
6668                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6669                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6670             }, {
6671                 re: /^#([\w-]+)/,
6672                 select: 'n = byId(n, null, "{1}");'
6673             },{
6674                 re: /^@([\w-]+)/,
6675                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6676             }
6677         ],
6678
6679         /**
6680          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6681          * 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;.
6682          */
6683         operators : {
6684             "=" : function(a, v){
6685                 return a == v;
6686             },
6687             "!=" : function(a, v){
6688                 return a != v;
6689             },
6690             "^=" : function(a, v){
6691                 return a && a.substr(0, v.length) == v;
6692             },
6693             "$=" : function(a, v){
6694                 return a && a.substr(a.length-v.length) == v;
6695             },
6696             "*=" : function(a, v){
6697                 return a && a.indexOf(v) !== -1;
6698             },
6699             "%=" : function(a, v){
6700                 return (a % v) == 0;
6701             },
6702             "|=" : function(a, v){
6703                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6704             },
6705             "~=" : function(a, v){
6706                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6707             }
6708         },
6709
6710         /**
6711          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6712          * and the argument (if any) supplied in the selector.
6713          */
6714         pseudos : {
6715             "first-child" : function(c){
6716                 var r = [], ri = -1, n;
6717                 for(var i = 0, ci; ci = n = c[i]; i++){
6718                     while((n = n.previousSibling) && n.nodeType != 1);
6719                     if(!n){
6720                         r[++ri] = ci;
6721                     }
6722                 }
6723                 return r;
6724             },
6725
6726             "last-child" : function(c){
6727                 var r = [], ri = -1, n;
6728                 for(var i = 0, ci; ci = n = c[i]; i++){
6729                     while((n = n.nextSibling) && n.nodeType != 1);
6730                     if(!n){
6731                         r[++ri] = ci;
6732                     }
6733                 }
6734                 return r;
6735             },
6736
6737             "nth-child" : function(c, a) {
6738                 var r = [], ri = -1;
6739                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6740                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6741                 for(var i = 0, n; n = c[i]; i++){
6742                     var pn = n.parentNode;
6743                     if (batch != pn._batch) {
6744                         var j = 0;
6745                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6746                             if(cn.nodeType == 1){
6747                                cn.nodeIndex = ++j;
6748                             }
6749                         }
6750                         pn._batch = batch;
6751                     }
6752                     if (f == 1) {
6753                         if (l == 0 || n.nodeIndex == l){
6754                             r[++ri] = n;
6755                         }
6756                     } else if ((n.nodeIndex + l) % f == 0){
6757                         r[++ri] = n;
6758                     }
6759                 }
6760
6761                 return r;
6762             },
6763
6764             "only-child" : function(c){
6765                 var r = [], ri = -1;;
6766                 for(var i = 0, ci; ci = c[i]; i++){
6767                     if(!prev(ci) && !next(ci)){
6768                         r[++ri] = ci;
6769                     }
6770                 }
6771                 return r;
6772             },
6773
6774             "empty" : function(c){
6775                 var r = [], ri = -1;
6776                 for(var i = 0, ci; ci = c[i]; i++){
6777                     var cns = ci.childNodes, j = 0, cn, empty = true;
6778                     while(cn = cns[j]){
6779                         ++j;
6780                         if(cn.nodeType == 1 || cn.nodeType == 3){
6781                             empty = false;
6782                             break;
6783                         }
6784                     }
6785                     if(empty){
6786                         r[++ri] = ci;
6787                     }
6788                 }
6789                 return r;
6790             },
6791
6792             "contains" : function(c, v){
6793                 var r = [], ri = -1;
6794                 for(var i = 0, ci; ci = c[i]; i++){
6795                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6796                         r[++ri] = ci;
6797                     }
6798                 }
6799                 return r;
6800             },
6801
6802             "nodeValue" : function(c, v){
6803                 var r = [], ri = -1;
6804                 for(var i = 0, ci; ci = c[i]; i++){
6805                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6806                         r[++ri] = ci;
6807                     }
6808                 }
6809                 return r;
6810             },
6811
6812             "checked" : function(c){
6813                 var r = [], ri = -1;
6814                 for(var i = 0, ci; ci = c[i]; i++){
6815                     if(ci.checked == true){
6816                         r[++ri] = ci;
6817                     }
6818                 }
6819                 return r;
6820             },
6821
6822             "not" : function(c, ss){
6823                 return Roo.DomQuery.filter(c, ss, true);
6824             },
6825
6826             "odd" : function(c){
6827                 return this["nth-child"](c, "odd");
6828             },
6829
6830             "even" : function(c){
6831                 return this["nth-child"](c, "even");
6832             },
6833
6834             "nth" : function(c, a){
6835                 return c[a-1] || [];
6836             },
6837
6838             "first" : function(c){
6839                 return c[0] || [];
6840             },
6841
6842             "last" : function(c){
6843                 return c[c.length-1] || [];
6844             },
6845
6846             "has" : function(c, ss){
6847                 var s = Roo.DomQuery.select;
6848                 var r = [], ri = -1;
6849                 for(var i = 0, ci; ci = c[i]; i++){
6850                     if(s(ss, ci).length > 0){
6851                         r[++ri] = ci;
6852                     }
6853                 }
6854                 return r;
6855             },
6856
6857             "next" : function(c, ss){
6858                 var is = Roo.DomQuery.is;
6859                 var r = [], ri = -1;
6860                 for(var i = 0, ci; ci = c[i]; i++){
6861                     var n = next(ci);
6862                     if(n && is(n, ss)){
6863                         r[++ri] = ci;
6864                     }
6865                 }
6866                 return r;
6867             },
6868
6869             "prev" : function(c, ss){
6870                 var is = Roo.DomQuery.is;
6871                 var r = [], ri = -1;
6872                 for(var i = 0, ci; ci = c[i]; i++){
6873                     var n = prev(ci);
6874                     if(n && is(n, ss)){
6875                         r[++ri] = ci;
6876                     }
6877                 }
6878                 return r;
6879             }
6880         }
6881     };
6882 }();
6883
6884 /**
6885  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6886  * @param {String} path The selector/xpath query
6887  * @param {Node} root (optional) The start of the query (defaults to document).
6888  * @return {Array}
6889  * @member Roo
6890  * @method query
6891  */
6892 Roo.query = Roo.DomQuery.select;
6893 /*
6894  * Based on:
6895  * Ext JS Library 1.1.1
6896  * Copyright(c) 2006-2007, Ext JS, LLC.
6897  *
6898  * Originally Released Under LGPL - original licence link has changed is not relivant.
6899  *
6900  * Fork - LGPL
6901  * <script type="text/javascript">
6902  */
6903
6904 /**
6905  * @class Roo.util.Observable
6906  * Base class that provides a common interface for publishing events. Subclasses are expected to
6907  * to have a property "events" with all the events defined.<br>
6908  * For example:
6909  * <pre><code>
6910  Employee = function(name){
6911     this.name = name;
6912     this.addEvents({
6913         "fired" : true,
6914         "quit" : true
6915     });
6916  }
6917  Roo.extend(Employee, Roo.util.Observable);
6918 </code></pre>
6919  * @param {Object} config properties to use (incuding events / listeners)
6920  */
6921
6922 Roo.util.Observable = function(cfg){
6923     
6924     cfg = cfg|| {};
6925     this.addEvents(cfg.events || {});
6926     if (cfg.events) {
6927         delete cfg.events; // make sure
6928     }
6929      
6930     Roo.apply(this, cfg);
6931     
6932     if(this.listeners){
6933         this.on(this.listeners);
6934         delete this.listeners;
6935     }
6936 };
6937 Roo.util.Observable.prototype = {
6938     /** 
6939  * @cfg {Object} listeners  list of events and functions to call for this object, 
6940  * For example :
6941  * <pre><code>
6942     listeners :  { 
6943        'click' : function(e) {
6944            ..... 
6945         } ,
6946         .... 
6947     } 
6948   </code></pre>
6949  */
6950     
6951     
6952     /**
6953      * Fires the specified event with the passed parameters (minus the event name).
6954      * @param {String} eventName
6955      * @param {Object...} args Variable number of parameters are passed to handlers
6956      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6957      */
6958     fireEvent : function(){
6959         var ce = this.events[arguments[0].toLowerCase()];
6960         if(typeof ce == "object"){
6961             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6962         }else{
6963             return true;
6964         }
6965     },
6966
6967     // private
6968     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6969
6970     /**
6971      * Appends an event handler to this component
6972      * @param {String}   eventName The type of event to listen for
6973      * @param {Function} handler The method the event invokes
6974      * @param {Object}   scope (optional) The scope in which to execute the handler
6975      * function. The handler function's "this" context.
6976      * @param {Object}   options (optional) An object containing handler configuration
6977      * properties. This may contain any of the following properties:<ul>
6978      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6979      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6980      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6981      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6982      * by the specified number of milliseconds. If the event fires again within that time, the original
6983      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6984      * </ul><br>
6985      * <p>
6986      * <b>Combining Options</b><br>
6987      * Using the options argument, it is possible to combine different types of listeners:<br>
6988      * <br>
6989      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6990                 <pre><code>
6991                 el.on('click', this.onClick, this, {
6992                         single: true,
6993                 delay: 100,
6994                 forumId: 4
6995                 });
6996                 </code></pre>
6997      * <p>
6998      * <b>Attaching multiple handlers in 1 call</b><br>
6999      * The method also allows for a single argument to be passed which is a config object containing properties
7000      * which specify multiple handlers.
7001      * <pre><code>
7002                 el.on({
7003                         'click': {
7004                         fn: this.onClick,
7005                         scope: this,
7006                         delay: 100
7007                 }, 
7008                 'mouseover': {
7009                         fn: this.onMouseOver,
7010                         scope: this
7011                 },
7012                 'mouseout': {
7013                         fn: this.onMouseOut,
7014                         scope: this
7015                 }
7016                 });
7017                 </code></pre>
7018      * <p>
7019      * Or a shorthand syntax which passes the same scope object to all handlers:
7020         <pre><code>
7021                 el.on({
7022                         'click': this.onClick,
7023                 'mouseover': this.onMouseOver,
7024                 'mouseout': this.onMouseOut,
7025                 scope: this
7026                 });
7027                 </code></pre>
7028      */
7029     addListener : function(eventName, fn, scope, o){
7030         if(typeof eventName == "object"){
7031             o = eventName;
7032             for(var e in o){
7033                 if(this.filterOptRe.test(e)){
7034                     continue;
7035                 }
7036                 if(typeof o[e] == "function"){
7037                     // shared options
7038                     this.addListener(e, o[e], o.scope,  o);
7039                 }else{
7040                     // individual options
7041                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
7042                 }
7043             }
7044             return;
7045         }
7046         o = (!o || typeof o == "boolean") ? {} : o;
7047         eventName = eventName.toLowerCase();
7048         var ce = this.events[eventName] || true;
7049         if(typeof ce == "boolean"){
7050             ce = new Roo.util.Event(this, eventName);
7051             this.events[eventName] = ce;
7052         }
7053         ce.addListener(fn, scope, o);
7054     },
7055
7056     /**
7057      * Removes a listener
7058      * @param {String}   eventName     The type of event to listen for
7059      * @param {Function} handler        The handler to remove
7060      * @param {Object}   scope  (optional) The scope (this object) for the handler
7061      */
7062     removeListener : function(eventName, fn, scope){
7063         var ce = this.events[eventName.toLowerCase()];
7064         if(typeof ce == "object"){
7065             ce.removeListener(fn, scope);
7066         }
7067     },
7068
7069     /**
7070      * Removes all listeners for this object
7071      */
7072     purgeListeners : function(){
7073         for(var evt in this.events){
7074             if(typeof this.events[evt] == "object"){
7075                  this.events[evt].clearListeners();
7076             }
7077         }
7078     },
7079
7080     relayEvents : function(o, events){
7081         var createHandler = function(ename){
7082             return function(){
7083                  
7084                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
7085             };
7086         };
7087         for(var i = 0, len = events.length; i < len; i++){
7088             var ename = events[i];
7089             if(!this.events[ename]){
7090                 this.events[ename] = true;
7091             };
7092             o.on(ename, createHandler(ename), this);
7093         }
7094     },
7095
7096     /**
7097      * Used to define events on this Observable
7098      * @param {Object} object The object with the events defined
7099      */
7100     addEvents : function(o){
7101         if(!this.events){
7102             this.events = {};
7103         }
7104         Roo.applyIf(this.events, o);
7105     },
7106
7107     /**
7108      * Checks to see if this object has any listeners for a specified event
7109      * @param {String} eventName The name of the event to check for
7110      * @return {Boolean} True if the event is being listened for, else false
7111      */
7112     hasListener : function(eventName){
7113         var e = this.events[eventName];
7114         return typeof e == "object" && e.listeners.length > 0;
7115     }
7116 };
7117 /**
7118  * Appends an event handler to this element (shorthand for addListener)
7119  * @param {String}   eventName     The type of event to listen for
7120  * @param {Function} handler        The method the event invokes
7121  * @param {Object}   scope (optional) The scope in which to execute the handler
7122  * function. The handler function's "this" context.
7123  * @param {Object}   options  (optional)
7124  * @method
7125  */
7126 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
7127 /**
7128  * Removes a listener (shorthand for removeListener)
7129  * @param {String}   eventName     The type of event to listen for
7130  * @param {Function} handler        The handler to remove
7131  * @param {Object}   scope  (optional) The scope (this object) for the handler
7132  * @method
7133  */
7134 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
7135
7136 /**
7137  * Starts capture on the specified Observable. All events will be passed
7138  * to the supplied function with the event name + standard signature of the event
7139  * <b>before</b> the event is fired. If the supplied function returns false,
7140  * the event will not fire.
7141  * @param {Observable} o The Observable to capture
7142  * @param {Function} fn The function to call
7143  * @param {Object} scope (optional) The scope (this object) for the fn
7144  * @static
7145  */
7146 Roo.util.Observable.capture = function(o, fn, scope){
7147     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
7148 };
7149
7150 /**
7151  * Removes <b>all</b> added captures from the Observable.
7152  * @param {Observable} o The Observable to release
7153  * @static
7154  */
7155 Roo.util.Observable.releaseCapture = function(o){
7156     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
7157 };
7158
7159 (function(){
7160
7161     var createBuffered = function(h, o, scope){
7162         var task = new Roo.util.DelayedTask();
7163         return function(){
7164             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
7165         };
7166     };
7167
7168     var createSingle = function(h, e, fn, scope){
7169         return function(){
7170             e.removeListener(fn, scope);
7171             return h.apply(scope, arguments);
7172         };
7173     };
7174
7175     var createDelayed = function(h, o, scope){
7176         return function(){
7177             var args = Array.prototype.slice.call(arguments, 0);
7178             setTimeout(function(){
7179                 h.apply(scope, args);
7180             }, o.delay || 10);
7181         };
7182     };
7183
7184     Roo.util.Event = function(obj, name){
7185         this.name = name;
7186         this.obj = obj;
7187         this.listeners = [];
7188     };
7189
7190     Roo.util.Event.prototype = {
7191         addListener : function(fn, scope, options){
7192             var o = options || {};
7193             scope = scope || this.obj;
7194             if(!this.isListening(fn, scope)){
7195                 var l = {fn: fn, scope: scope, options: o};
7196                 var h = fn;
7197                 if(o.delay){
7198                     h = createDelayed(h, o, scope);
7199                 }
7200                 if(o.single){
7201                     h = createSingle(h, this, fn, scope);
7202                 }
7203                 if(o.buffer){
7204                     h = createBuffered(h, o, scope);
7205                 }
7206                 l.fireFn = h;
7207                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
7208                     this.listeners.push(l);
7209                 }else{
7210                     this.listeners = this.listeners.slice(0);
7211                     this.listeners.push(l);
7212                 }
7213             }
7214         },
7215
7216         findListener : function(fn, scope){
7217             scope = scope || this.obj;
7218             var ls = this.listeners;
7219             for(var i = 0, len = ls.length; i < len; i++){
7220                 var l = ls[i];
7221                 if(l.fn == fn && l.scope == scope){
7222                     return i;
7223                 }
7224             }
7225             return -1;
7226         },
7227
7228         isListening : function(fn, scope){
7229             return this.findListener(fn, scope) != -1;
7230         },
7231
7232         removeListener : function(fn, scope){
7233             var index;
7234             if((index = this.findListener(fn, scope)) != -1){
7235                 if(!this.firing){
7236                     this.listeners.splice(index, 1);
7237                 }else{
7238                     this.listeners = this.listeners.slice(0);
7239                     this.listeners.splice(index, 1);
7240                 }
7241                 return true;
7242             }
7243             return false;
7244         },
7245
7246         clearListeners : function(){
7247             this.listeners = [];
7248         },
7249
7250         fire : function(){
7251             var ls = this.listeners, scope, len = ls.length;
7252             if(len > 0){
7253                 this.firing = true;
7254                 var args = Array.prototype.slice.call(arguments, 0);                
7255                 for(var i = 0; i < len; i++){
7256                     var l = ls[i];
7257                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
7258                         this.firing = false;
7259                         return false;
7260                     }
7261                 }
7262                 this.firing = false;
7263             }
7264             return true;
7265         }
7266     };
7267 })();/*
7268  * RooJS Library 
7269  * Copyright(c) 2007-2017, Roo J Solutions Ltd
7270  *
7271  * Licence LGPL 
7272  *
7273  */
7274  
7275 /**
7276  * @class Roo.Document
7277  * @extends Roo.util.Observable
7278  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
7279  * 
7280  * @param {Object} config the methods and properties of the 'base' class for the application.
7281  * 
7282  *  Generic Page handler - implement this to start your app..
7283  * 
7284  * eg.
7285  *  MyProject = new Roo.Document({
7286         events : {
7287             'load' : true // your events..
7288         },
7289         listeners : {
7290             'ready' : function() {
7291                 // fired on Roo.onReady()
7292             }
7293         }
7294  * 
7295  */
7296 Roo.Document = function(cfg) {
7297      
7298     this.addEvents({ 
7299         'ready' : true
7300     });
7301     Roo.util.Observable.call(this,cfg);
7302     
7303     var _this = this;
7304     
7305     Roo.onReady(function() {
7306         _this.fireEvent('ready');
7307     },null,false);
7308     
7309     
7310 }
7311
7312 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
7313  * Based on:
7314  * Ext JS Library 1.1.1
7315  * Copyright(c) 2006-2007, Ext JS, LLC.
7316  *
7317  * Originally Released Under LGPL - original licence link has changed is not relivant.
7318  *
7319  * Fork - LGPL
7320  * <script type="text/javascript">
7321  */
7322
7323 /**
7324  * @class Roo.EventManager
7325  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7326  * several useful events directly.
7327  * See {@link Roo.EventObject} for more details on normalized event objects.
7328  * @static
7329  */
7330 Roo.EventManager = function(){
7331     var docReadyEvent, docReadyProcId, docReadyState = false;
7332     var resizeEvent, resizeTask, textEvent, textSize;
7333     var E = Roo.lib.Event;
7334     var D = Roo.lib.Dom;
7335
7336     
7337     
7338
7339     var fireDocReady = function(){
7340         if(!docReadyState){
7341             docReadyState = true;
7342             Roo.isReady = true;
7343             if(docReadyProcId){
7344                 clearInterval(docReadyProcId);
7345             }
7346             if(Roo.isGecko || Roo.isOpera) {
7347                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7348             }
7349             if(Roo.isIE){
7350                 var defer = document.getElementById("ie-deferred-loader");
7351                 if(defer){
7352                     defer.onreadystatechange = null;
7353                     defer.parentNode.removeChild(defer);
7354                 }
7355             }
7356             if(docReadyEvent){
7357                 docReadyEvent.fire();
7358                 docReadyEvent.clearListeners();
7359             }
7360         }
7361     };
7362     
7363     var initDocReady = function(){
7364         docReadyEvent = new Roo.util.Event();
7365         if(Roo.isGecko || Roo.isOpera) {
7366             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7367         }else if(Roo.isIE){
7368             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7369             var defer = document.getElementById("ie-deferred-loader");
7370             defer.onreadystatechange = function(){
7371                 if(this.readyState == "complete"){
7372                     fireDocReady();
7373                 }
7374             };
7375         }else if(Roo.isSafari){ 
7376             docReadyProcId = setInterval(function(){
7377                 var rs = document.readyState;
7378                 if(rs == "complete") {
7379                     fireDocReady();     
7380                  }
7381             }, 10);
7382         }
7383         // no matter what, make sure it fires on load
7384         E.on(window, "load", fireDocReady);
7385     };
7386
7387     var createBuffered = function(h, o){
7388         var task = new Roo.util.DelayedTask(h);
7389         return function(e){
7390             // create new event object impl so new events don't wipe out properties
7391             e = new Roo.EventObjectImpl(e);
7392             task.delay(o.buffer, h, null, [e]);
7393         };
7394     };
7395
7396     var createSingle = function(h, el, ename, fn){
7397         return function(e){
7398             Roo.EventManager.removeListener(el, ename, fn);
7399             h(e);
7400         };
7401     };
7402
7403     var createDelayed = function(h, o){
7404         return function(e){
7405             // create new event object impl so new events don't wipe out properties
7406             e = new Roo.EventObjectImpl(e);
7407             setTimeout(function(){
7408                 h(e);
7409             }, o.delay || 10);
7410         };
7411     };
7412     var transitionEndVal = false;
7413     
7414     var transitionEnd = function()
7415     {
7416         if (transitionEndVal) {
7417             return transitionEndVal;
7418         }
7419         var el = document.createElement('div');
7420
7421         var transEndEventNames = {
7422             WebkitTransition : 'webkitTransitionEnd',
7423             MozTransition    : 'transitionend',
7424             OTransition      : 'oTransitionEnd otransitionend',
7425             transition       : 'transitionend'
7426         };
7427     
7428         for (var name in transEndEventNames) {
7429             if (el.style[name] !== undefined) {
7430                 transitionEndVal = transEndEventNames[name];
7431                 return  transitionEndVal ;
7432             }
7433         }
7434     }
7435     
7436   
7437
7438     var listen = function(element, ename, opt, fn, scope)
7439     {
7440         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7441         fn = fn || o.fn; scope = scope || o.scope;
7442         var el = Roo.getDom(element);
7443         
7444         
7445         if(!el){
7446             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7447         }
7448         
7449         if (ename == 'transitionend') {
7450             ename = transitionEnd();
7451         }
7452         var h = function(e){
7453             e = Roo.EventObject.setEvent(e);
7454             var t;
7455             if(o.delegate){
7456                 t = e.getTarget(o.delegate, el);
7457                 if(!t){
7458                     return;
7459                 }
7460             }else{
7461                 t = e.target;
7462             }
7463             if(o.stopEvent === true){
7464                 e.stopEvent();
7465             }
7466             if(o.preventDefault === true){
7467                e.preventDefault();
7468             }
7469             if(o.stopPropagation === true){
7470                 e.stopPropagation();
7471             }
7472
7473             if(o.normalized === false){
7474                 e = e.browserEvent;
7475             }
7476
7477             fn.call(scope || el, e, t, o);
7478         };
7479         if(o.delay){
7480             h = createDelayed(h, o);
7481         }
7482         if(o.single){
7483             h = createSingle(h, el, ename, fn);
7484         }
7485         if(o.buffer){
7486             h = createBuffered(h, o);
7487         }
7488         
7489         fn._handlers = fn._handlers || [];
7490         
7491         
7492         fn._handlers.push([Roo.id(el), ename, h]);
7493         
7494         
7495          
7496         E.on(el, ename, h); // this adds the actuall listener to the object..
7497         
7498         
7499         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7500             el.addEventListener("DOMMouseScroll", h, false);
7501             E.on(window, 'unload', function(){
7502                 el.removeEventListener("DOMMouseScroll", h, false);
7503             });
7504         }
7505         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7506             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7507         }
7508         return h;
7509     };
7510
7511     var stopListening = function(el, ename, fn){
7512         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7513         if(hds){
7514             for(var i = 0, len = hds.length; i < len; i++){
7515                 var h = hds[i];
7516                 if(h[0] == id && h[1] == ename){
7517                     hd = h[2];
7518                     hds.splice(i, 1);
7519                     break;
7520                 }
7521             }
7522         }
7523         E.un(el, ename, hd);
7524         el = Roo.getDom(el);
7525         if(ename == "mousewheel" && el.addEventListener){
7526             el.removeEventListener("DOMMouseScroll", hd, false);
7527         }
7528         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7529             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7530         }
7531     };
7532
7533     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7534     
7535     var pub = {
7536         
7537         
7538         /** 
7539          * Fix for doc tools
7540          * @scope Roo.EventManager
7541          */
7542         
7543         
7544         /** 
7545          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7546          * object with a Roo.EventObject
7547          * @param {Function} fn        The method the event invokes
7548          * @param {Object}   scope    An object that becomes the scope of the handler
7549          * @param {boolean}  override If true, the obj passed in becomes
7550          *                             the execution scope of the listener
7551          * @return {Function} The wrapped function
7552          * @deprecated
7553          */
7554         wrap : function(fn, scope, override){
7555             return function(e){
7556                 Roo.EventObject.setEvent(e);
7557                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7558             };
7559         },
7560         
7561         /**
7562      * Appends an event handler to an element (shorthand for addListener)
7563      * @param {String/HTMLElement}   element        The html element or id to assign the
7564      * @param {String}   eventName The type of event to listen for
7565      * @param {Function} handler The method the event invokes
7566      * @param {Object}   scope (optional) The scope in which to execute the handler
7567      * function. The handler function's "this" context.
7568      * @param {Object}   options (optional) An object containing handler configuration
7569      * properties. This may contain any of the following properties:<ul>
7570      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7571      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7572      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7573      * <li>preventDefault {Boolean} True to prevent the default action</li>
7574      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7575      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7576      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7577      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7578      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7579      * by the specified number of milliseconds. If the event fires again within that time, the original
7580      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7581      * </ul><br>
7582      * <p>
7583      * <b>Combining Options</b><br>
7584      * Using the options argument, it is possible to combine different types of listeners:<br>
7585      * <br>
7586      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7587      * Code:<pre><code>
7588 el.on('click', this.onClick, this, {
7589     single: true,
7590     delay: 100,
7591     stopEvent : true,
7592     forumId: 4
7593 });</code></pre>
7594      * <p>
7595      * <b>Attaching multiple handlers in 1 call</b><br>
7596       * The method also allows for a single argument to be passed which is a config object containing properties
7597      * which specify multiple handlers.
7598      * <p>
7599      * Code:<pre><code>
7600 el.on({
7601     'click' : {
7602         fn: this.onClick
7603         scope: this,
7604         delay: 100
7605     },
7606     'mouseover' : {
7607         fn: this.onMouseOver
7608         scope: this
7609     },
7610     'mouseout' : {
7611         fn: this.onMouseOut
7612         scope: this
7613     }
7614 });</code></pre>
7615      * <p>
7616      * Or a shorthand syntax:<br>
7617      * Code:<pre><code>
7618 el.on({
7619     'click' : this.onClick,
7620     'mouseover' : this.onMouseOver,
7621     'mouseout' : this.onMouseOut
7622     scope: this
7623 });</code></pre>
7624      */
7625         addListener : function(element, eventName, fn, scope, options){
7626             if(typeof eventName == "object"){
7627                 var o = eventName;
7628                 for(var e in o){
7629                     if(propRe.test(e)){
7630                         continue;
7631                     }
7632                     if(typeof o[e] == "function"){
7633                         // shared options
7634                         listen(element, e, o, o[e], o.scope);
7635                     }else{
7636                         // individual options
7637                         listen(element, e, o[e]);
7638                     }
7639                 }
7640                 return;
7641             }
7642             return listen(element, eventName, options, fn, scope);
7643         },
7644         
7645         /**
7646          * Removes an event handler
7647          *
7648          * @param {String/HTMLElement}   element        The id or html element to remove the 
7649          *                             event from
7650          * @param {String}   eventName     The type of event
7651          * @param {Function} fn
7652          * @return {Boolean} True if a listener was actually removed
7653          */
7654         removeListener : function(element, eventName, fn){
7655             return stopListening(element, eventName, fn);
7656         },
7657         
7658         /**
7659          * Fires when the document is ready (before onload and before images are loaded). Can be 
7660          * accessed shorthanded Roo.onReady().
7661          * @param {Function} fn        The method the event invokes
7662          * @param {Object}   scope    An  object that becomes the scope of the handler
7663          * @param {boolean}  options
7664          */
7665         onDocumentReady : function(fn, scope, options){
7666             if(docReadyState){ // if it already fired
7667                 docReadyEvent.addListener(fn, scope, options);
7668                 docReadyEvent.fire();
7669                 docReadyEvent.clearListeners();
7670                 return;
7671             }
7672             if(!docReadyEvent){
7673                 initDocReady();
7674             }
7675             docReadyEvent.addListener(fn, scope, options);
7676         },
7677         
7678         /**
7679          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7680          * @param {Function} fn        The method the event invokes
7681          * @param {Object}   scope    An object that becomes the scope of the handler
7682          * @param {boolean}  options
7683          */
7684         onWindowResize : function(fn, scope, options)
7685         {
7686             if(!resizeEvent){
7687                 resizeEvent = new Roo.util.Event();
7688                 resizeTask = new Roo.util.DelayedTask(function(){
7689                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7690                 });
7691                 E.on(window, "resize", function()
7692                 {
7693                     if (Roo.isIE) {
7694                         resizeTask.delay(50);
7695                     } else {
7696                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7697                     }
7698                 });
7699             }
7700             resizeEvent.addListener(fn, scope, options);
7701         },
7702
7703         /**
7704          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7705          * @param {Function} fn        The method the event invokes
7706          * @param {Object}   scope    An object that becomes the scope of the handler
7707          * @param {boolean}  options
7708          */
7709         onTextResize : function(fn, scope, options){
7710             if(!textEvent){
7711                 textEvent = new Roo.util.Event();
7712                 var textEl = new Roo.Element(document.createElement('div'));
7713                 textEl.dom.className = 'x-text-resize';
7714                 textEl.dom.innerHTML = 'X';
7715                 textEl.appendTo(document.body);
7716                 textSize = textEl.dom.offsetHeight;
7717                 setInterval(function(){
7718                     if(textEl.dom.offsetHeight != textSize){
7719                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7720                     }
7721                 }, this.textResizeInterval);
7722             }
7723             textEvent.addListener(fn, scope, options);
7724         },
7725
7726         /**
7727          * Removes the passed window resize listener.
7728          * @param {Function} fn        The method the event invokes
7729          * @param {Object}   scope    The scope of handler
7730          */
7731         removeResizeListener : function(fn, scope){
7732             if(resizeEvent){
7733                 resizeEvent.removeListener(fn, scope);
7734             }
7735         },
7736
7737         // private
7738         fireResize : function(){
7739             if(resizeEvent){
7740                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7741             }   
7742         },
7743         /**
7744          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7745          */
7746         ieDeferSrc : false,
7747         /**
7748          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7749          */
7750         textResizeInterval : 50
7751     };
7752     
7753     /**
7754      * Fix for doc tools
7755      * @scopeAlias pub=Roo.EventManager
7756      */
7757     
7758      /**
7759      * Appends an event handler to an element (shorthand for addListener)
7760      * @param {String/HTMLElement}   element        The html element or id to assign the
7761      * @param {String}   eventName The type of event to listen for
7762      * @param {Function} handler The method the event invokes
7763      * @param {Object}   scope (optional) The scope in which to execute the handler
7764      * function. The handler function's "this" context.
7765      * @param {Object}   options (optional) An object containing handler configuration
7766      * properties. This may contain any of the following properties:<ul>
7767      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7768      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7769      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7770      * <li>preventDefault {Boolean} True to prevent the default action</li>
7771      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7772      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7773      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7774      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7775      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7776      * by the specified number of milliseconds. If the event fires again within that time, the original
7777      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7778      * </ul><br>
7779      * <p>
7780      * <b>Combining Options</b><br>
7781      * Using the options argument, it is possible to combine different types of listeners:<br>
7782      * <br>
7783      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7784      * Code:<pre><code>
7785 el.on('click', this.onClick, this, {
7786     single: true,
7787     delay: 100,
7788     stopEvent : true,
7789     forumId: 4
7790 });</code></pre>
7791      * <p>
7792      * <b>Attaching multiple handlers in 1 call</b><br>
7793       * The method also allows for a single argument to be passed which is a config object containing properties
7794      * which specify multiple handlers.
7795      * <p>
7796      * Code:<pre><code>
7797 el.on({
7798     'click' : {
7799         fn: this.onClick
7800         scope: this,
7801         delay: 100
7802     },
7803     'mouseover' : {
7804         fn: this.onMouseOver
7805         scope: this
7806     },
7807     'mouseout' : {
7808         fn: this.onMouseOut
7809         scope: this
7810     }
7811 });</code></pre>
7812      * <p>
7813      * Or a shorthand syntax:<br>
7814      * Code:<pre><code>
7815 el.on({
7816     'click' : this.onClick,
7817     'mouseover' : this.onMouseOver,
7818     'mouseout' : this.onMouseOut
7819     scope: this
7820 });</code></pre>
7821      */
7822     pub.on = pub.addListener;
7823     pub.un = pub.removeListener;
7824
7825     pub.stoppedMouseDownEvent = new Roo.util.Event();
7826     return pub;
7827 }();
7828 /**
7829   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7830   * @param {Function} fn        The method the event invokes
7831   * @param {Object}   scope    An  object that becomes the scope of the handler
7832   * @param {boolean}  override If true, the obj passed in becomes
7833   *                             the execution scope of the listener
7834   * @member Roo
7835   * @method onReady
7836  */
7837 Roo.onReady = Roo.EventManager.onDocumentReady;
7838
7839 Roo.onReady(function(){
7840     var bd = Roo.get(document.body);
7841     if(!bd){ return; }
7842
7843     var cls = [
7844             Roo.isIE ? "roo-ie"
7845             : Roo.isIE11 ? "roo-ie11"
7846             : Roo.isEdge ? "roo-edge"
7847             : Roo.isGecko ? "roo-gecko"
7848             : Roo.isOpera ? "roo-opera"
7849             : Roo.isSafari ? "roo-safari" : ""];
7850
7851     if(Roo.isMac){
7852         cls.push("roo-mac");
7853     }
7854     if(Roo.isLinux){
7855         cls.push("roo-linux");
7856     }
7857     if(Roo.isIOS){
7858         cls.push("roo-ios");
7859     }
7860     if(Roo.isTouch){
7861         cls.push("roo-touch");
7862     }
7863     if(Roo.isBorderBox){
7864         cls.push('roo-border-box');
7865     }
7866     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7867         var p = bd.dom.parentNode;
7868         if(p){
7869             p.className += ' roo-strict';
7870         }
7871     }
7872     bd.addClass(cls.join(' '));
7873 });
7874
7875 /**
7876  * @class Roo.EventObject
7877  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7878  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7879  * Example:
7880  * <pre><code>
7881  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7882     e.preventDefault();
7883     var target = e.getTarget();
7884     ...
7885  }
7886  var myDiv = Roo.get("myDiv");
7887  myDiv.on("click", handleClick);
7888  //or
7889  Roo.EventManager.on("myDiv", 'click', handleClick);
7890  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7891  </code></pre>
7892  * @static
7893  */
7894 Roo.EventObject = function(){
7895     
7896     var E = Roo.lib.Event;
7897     
7898     // safari keypress events for special keys return bad keycodes
7899     var safariKeys = {
7900         63234 : 37, // left
7901         63235 : 39, // right
7902         63232 : 38, // up
7903         63233 : 40, // down
7904         63276 : 33, // page up
7905         63277 : 34, // page down
7906         63272 : 46, // delete
7907         63273 : 36, // home
7908         63275 : 35  // end
7909     };
7910
7911     // normalize button clicks
7912     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7913                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7914
7915     Roo.EventObjectImpl = function(e){
7916         if(e){
7917             this.setEvent(e.browserEvent || e);
7918         }
7919     };
7920     Roo.EventObjectImpl.prototype = {
7921         /**
7922          * Used to fix doc tools.
7923          * @scope Roo.EventObject.prototype
7924          */
7925             
7926
7927         
7928         
7929         /** The normal browser event */
7930         browserEvent : null,
7931         /** The button pressed in a mouse event */
7932         button : -1,
7933         /** True if the shift key was down during the event */
7934         shiftKey : false,
7935         /** True if the control key was down during the event */
7936         ctrlKey : false,
7937         /** True if the alt key was down during the event */
7938         altKey : false,
7939
7940         /** Key constant 
7941         * @type Number */
7942         BACKSPACE : 8,
7943         /** Key constant 
7944         * @type Number */
7945         TAB : 9,
7946         /** Key constant 
7947         * @type Number */
7948         RETURN : 13,
7949         /** Key constant 
7950         * @type Number */
7951         ENTER : 13,
7952         /** Key constant 
7953         * @type Number */
7954         SHIFT : 16,
7955         /** Key constant 
7956         * @type Number */
7957         CONTROL : 17,
7958         /** Key constant 
7959         * @type Number */
7960         ESC : 27,
7961         /** Key constant 
7962         * @type Number */
7963         SPACE : 32,
7964         /** Key constant 
7965         * @type Number */
7966         PAGEUP : 33,
7967         /** Key constant 
7968         * @type Number */
7969         PAGEDOWN : 34,
7970         /** Key constant 
7971         * @type Number */
7972         END : 35,
7973         /** Key constant 
7974         * @type Number */
7975         HOME : 36,
7976         /** Key constant 
7977         * @type Number */
7978         LEFT : 37,
7979         /** Key constant 
7980         * @type Number */
7981         UP : 38,
7982         /** Key constant 
7983         * @type Number */
7984         RIGHT : 39,
7985         /** Key constant 
7986         * @type Number */
7987         DOWN : 40,
7988         /** Key constant 
7989         * @type Number */
7990         DELETE : 46,
7991         /** Key constant 
7992         * @type Number */
7993         F5 : 116,
7994
7995            /** @private */
7996         setEvent : function(e){
7997             if(e == this || (e && e.browserEvent)){ // already wrapped
7998                 return e;
7999             }
8000             this.browserEvent = e;
8001             if(e){
8002                 // normalize buttons
8003                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
8004                 if(e.type == 'click' && this.button == -1){
8005                     this.button = 0;
8006                 }
8007                 this.type = e.type;
8008                 this.shiftKey = e.shiftKey;
8009                 // mac metaKey behaves like ctrlKey
8010                 this.ctrlKey = e.ctrlKey || e.metaKey;
8011                 this.altKey = e.altKey;
8012                 // in getKey these will be normalized for the mac
8013                 this.keyCode = e.keyCode;
8014                 // keyup warnings on firefox.
8015                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
8016                 // cache the target for the delayed and or buffered events
8017                 this.target = E.getTarget(e);
8018                 // same for XY
8019                 this.xy = E.getXY(e);
8020             }else{
8021                 this.button = -1;
8022                 this.shiftKey = false;
8023                 this.ctrlKey = false;
8024                 this.altKey = false;
8025                 this.keyCode = 0;
8026                 this.charCode =0;
8027                 this.target = null;
8028                 this.xy = [0, 0];
8029             }
8030             return this;
8031         },
8032
8033         /**
8034          * Stop the event (preventDefault and stopPropagation)
8035          */
8036         stopEvent : function(){
8037             if(this.browserEvent){
8038                 if(this.browserEvent.type == 'mousedown'){
8039                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8040                 }
8041                 E.stopEvent(this.browserEvent);
8042             }
8043         },
8044
8045         /**
8046          * Prevents the browsers default handling of the event.
8047          */
8048         preventDefault : function(){
8049             if(this.browserEvent){
8050                 E.preventDefault(this.browserEvent);
8051             }
8052         },
8053
8054         /** @private */
8055         isNavKeyPress : function(){
8056             var k = this.keyCode;
8057             k = Roo.isSafari ? (safariKeys[k] || k) : k;
8058             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
8059         },
8060
8061         isSpecialKey : function(){
8062             var k = this.keyCode;
8063             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
8064             (k == 16) || (k == 17) ||
8065             (k >= 18 && k <= 20) ||
8066             (k >= 33 && k <= 35) ||
8067             (k >= 36 && k <= 39) ||
8068             (k >= 44 && k <= 45);
8069         },
8070         /**
8071          * Cancels bubbling of the event.
8072          */
8073         stopPropagation : function(){
8074             if(this.browserEvent){
8075                 if(this.type == 'mousedown'){
8076                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
8077                 }
8078                 E.stopPropagation(this.browserEvent);
8079             }
8080         },
8081
8082         /**
8083          * Gets the key code for the event.
8084          * @return {Number}
8085          */
8086         getCharCode : function(){
8087             return this.charCode || this.keyCode;
8088         },
8089
8090         /**
8091          * Returns a normalized keyCode for the event.
8092          * @return {Number} The key code
8093          */
8094         getKey : function(){
8095             var k = this.keyCode || this.charCode;
8096             return Roo.isSafari ? (safariKeys[k] || k) : k;
8097         },
8098
8099         /**
8100          * Gets the x coordinate of the event.
8101          * @return {Number}
8102          */
8103         getPageX : function(){
8104             return this.xy[0];
8105         },
8106
8107         /**
8108          * Gets the y coordinate of the event.
8109          * @return {Number}
8110          */
8111         getPageY : function(){
8112             return this.xy[1];
8113         },
8114
8115         /**
8116          * Gets the time of the event.
8117          * @return {Number}
8118          */
8119         getTime : function(){
8120             if(this.browserEvent){
8121                 return E.getTime(this.browserEvent);
8122             }
8123             return null;
8124         },
8125
8126         /**
8127          * Gets the page coordinates of the event.
8128          * @return {Array} The xy values like [x, y]
8129          */
8130         getXY : function(){
8131             return this.xy;
8132         },
8133
8134         /**
8135          * Gets the target for the event.
8136          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
8137          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8138                 search as a number or element (defaults to 10 || document.body)
8139          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8140          * @return {HTMLelement}
8141          */
8142         getTarget : function(selector, maxDepth, returnEl){
8143             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
8144         },
8145         /**
8146          * Gets the related target.
8147          * @return {HTMLElement}
8148          */
8149         getRelatedTarget : function(){
8150             if(this.browserEvent){
8151                 return E.getRelatedTarget(this.browserEvent);
8152             }
8153             return null;
8154         },
8155
8156         /**
8157          * Normalizes mouse wheel delta across browsers
8158          * @return {Number} The delta
8159          */
8160         getWheelDelta : function(){
8161             var e = this.browserEvent;
8162             var delta = 0;
8163             if(e.wheelDelta){ /* IE/Opera. */
8164                 delta = e.wheelDelta/120;
8165             }else if(e.detail){ /* Mozilla case. */
8166                 delta = -e.detail/3;
8167             }
8168             return delta;
8169         },
8170
8171         /**
8172          * Returns true if the control, meta, shift or alt key was pressed during this event.
8173          * @return {Boolean}
8174          */
8175         hasModifier : function(){
8176             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
8177         },
8178
8179         /**
8180          * Returns true if the target of this event equals el or is a child of el
8181          * @param {String/HTMLElement/Element} el
8182          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
8183          * @return {Boolean}
8184          */
8185         within : function(el, related){
8186             var t = this[related ? "getRelatedTarget" : "getTarget"]();
8187             return t && Roo.fly(el).contains(t);
8188         },
8189
8190         getPoint : function(){
8191             return new Roo.lib.Point(this.xy[0], this.xy[1]);
8192         }
8193     };
8194
8195     return new Roo.EventObjectImpl();
8196 }();
8197             
8198     /*
8199  * Based on:
8200  * Ext JS Library 1.1.1
8201  * Copyright(c) 2006-2007, Ext JS, LLC.
8202  *
8203  * Originally Released Under LGPL - original licence link has changed is not relivant.
8204  *
8205  * Fork - LGPL
8206  * <script type="text/javascript">
8207  */
8208
8209  
8210 // was in Composite Element!??!?!
8211  
8212 (function(){
8213     var D = Roo.lib.Dom;
8214     var E = Roo.lib.Event;
8215     var A = Roo.lib.Anim;
8216
8217     // local style camelizing for speed
8218     var propCache = {};
8219     var camelRe = /(-[a-z])/gi;
8220     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
8221     var view = document.defaultView;
8222
8223 /**
8224  * @class Roo.Element
8225  * Represents an Element in the DOM.<br><br>
8226  * Usage:<br>
8227 <pre><code>
8228 var el = Roo.get("my-div");
8229
8230 // or with getEl
8231 var el = getEl("my-div");
8232
8233 // or with a DOM element
8234 var el = Roo.get(myDivElement);
8235 </code></pre>
8236  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
8237  * each call instead of constructing a new one.<br><br>
8238  * <b>Animations</b><br />
8239  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
8240  * should either be a boolean (true) or an object literal with animation options. The animation options are:
8241 <pre>
8242 Option    Default   Description
8243 --------- --------  ---------------------------------------------
8244 duration  .35       The duration of the animation in seconds
8245 easing    easeOut   The YUI easing method
8246 callback  none      A function to execute when the anim completes
8247 scope     this      The scope (this) of the callback function
8248 </pre>
8249 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
8250 * manipulate the animation. Here's an example:
8251 <pre><code>
8252 var el = Roo.get("my-div");
8253
8254 // no animation
8255 el.setWidth(100);
8256
8257 // default animation
8258 el.setWidth(100, true);
8259
8260 // animation with some options set
8261 el.setWidth(100, {
8262     duration: 1,
8263     callback: this.foo,
8264     scope: this
8265 });
8266
8267 // using the "anim" property to get the Anim object
8268 var opt = {
8269     duration: 1,
8270     callback: this.foo,
8271     scope: this
8272 };
8273 el.setWidth(100, opt);
8274 ...
8275 if(opt.anim.isAnimated()){
8276     opt.anim.stop();
8277 }
8278 </code></pre>
8279 * <b> Composite (Collections of) Elements</b><br />
8280  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
8281  * @constructor Create a new Element directly.
8282  * @param {String/HTMLElement} element
8283  * @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).
8284  */
8285     Roo.Element = function(element, forceNew)
8286     {
8287         var dom = typeof element == "string" ?
8288                 document.getElementById(element) : element;
8289         
8290         this.listeners = {};
8291         
8292         if(!dom){ // invalid id/element
8293             return null;
8294         }
8295         var id = dom.id;
8296         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
8297             return Roo.Element.cache[id];
8298         }
8299
8300         /**
8301          * The DOM element
8302          * @type HTMLElement
8303          */
8304         this.dom = dom;
8305
8306         /**
8307          * The DOM element ID
8308          * @type String
8309          */
8310         this.id = id || Roo.id(dom);
8311         
8312         return this; // assumed for cctor?
8313     };
8314
8315     var El = Roo.Element;
8316
8317     El.prototype = {
8318         /**
8319          * The element's default display mode  (defaults to "") 
8320          * @type String
8321          */
8322         originalDisplay : "",
8323
8324         
8325         // note this is overridden in BS version..
8326         visibilityMode : 1, 
8327         /**
8328          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8329          * @type String
8330          */
8331         defaultUnit : "px",
8332         
8333         /**
8334          * Sets the element's visibility mode. When setVisible() is called it
8335          * will use this to determine whether to set the visibility or the display property.
8336          * @param visMode Element.VISIBILITY or Element.DISPLAY
8337          * @return {Roo.Element} this
8338          */
8339         setVisibilityMode : function(visMode){
8340             this.visibilityMode = visMode;
8341             return this;
8342         },
8343         /**
8344          * Convenience method for setVisibilityMode(Element.DISPLAY)
8345          * @param {String} display (optional) What to set display to when visible
8346          * @return {Roo.Element} this
8347          */
8348         enableDisplayMode : function(display){
8349             this.setVisibilityMode(El.DISPLAY);
8350             if(typeof display != "undefined") { this.originalDisplay = display; }
8351             return this;
8352         },
8353
8354         /**
8355          * 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)
8356          * @param {String} selector The simple selector to test
8357          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8358                 search as a number or element (defaults to 10 || document.body)
8359          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8360          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8361          */
8362         findParent : function(simpleSelector, maxDepth, returnEl){
8363             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8364             maxDepth = maxDepth || 50;
8365             if(typeof maxDepth != "number"){
8366                 stopEl = Roo.getDom(maxDepth);
8367                 maxDepth = 10;
8368             }
8369             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8370                 if(dq.is(p, simpleSelector)){
8371                     return returnEl ? Roo.get(p) : p;
8372                 }
8373                 depth++;
8374                 p = p.parentNode;
8375             }
8376             return null;
8377         },
8378
8379
8380         /**
8381          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8382          * @param {String} selector The simple selector to test
8383          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8384                 search as a number or element (defaults to 10 || document.body)
8385          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8386          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8387          */
8388         findParentNode : function(simpleSelector, maxDepth, returnEl){
8389             var p = Roo.fly(this.dom.parentNode, '_internal');
8390             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8391         },
8392         
8393         /**
8394          * Looks at  the scrollable parent element
8395          */
8396         findScrollableParent : function()
8397         {
8398             var overflowRegex = /(auto|scroll)/;
8399             
8400             if(this.getStyle('position') === 'fixed'){
8401                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8402             }
8403             
8404             var excludeStaticParent = this.getStyle('position') === "absolute";
8405             
8406             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8407                 
8408                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8409                     continue;
8410                 }
8411                 
8412                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8413                     return parent;
8414                 }
8415                 
8416                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8417                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8418                 }
8419             }
8420             
8421             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8422         },
8423
8424         /**
8425          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8426          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8427          * @param {String} selector The simple selector to test
8428          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8429                 search as a number or element (defaults to 10 || document.body)
8430          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8431          */
8432         up : function(simpleSelector, maxDepth){
8433             return this.findParentNode(simpleSelector, maxDepth, true);
8434         },
8435
8436
8437
8438         /**
8439          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8440          * @param {String} selector The simple selector to test
8441          * @return {Boolean} True if this element matches the selector, else false
8442          */
8443         is : function(simpleSelector){
8444             return Roo.DomQuery.is(this.dom, simpleSelector);
8445         },
8446
8447         /**
8448          * Perform animation on this element.
8449          * @param {Object} args The YUI animation control args
8450          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8451          * @param {Function} onComplete (optional) Function to call when animation completes
8452          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8453          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8454          * @return {Roo.Element} this
8455          */
8456         animate : function(args, duration, onComplete, easing, animType){
8457             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8458             return this;
8459         },
8460
8461         /*
8462          * @private Internal animation call
8463          */
8464         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8465             animType = animType || 'run';
8466             opt = opt || {};
8467             var anim = Roo.lib.Anim[animType](
8468                 this.dom, args,
8469                 (opt.duration || defaultDur) || .35,
8470                 (opt.easing || defaultEase) || 'easeOut',
8471                 function(){
8472                     Roo.callback(cb, this);
8473                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8474                 },
8475                 this
8476             );
8477             opt.anim = anim;
8478             return anim;
8479         },
8480
8481         // private legacy anim prep
8482         preanim : function(a, i){
8483             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8484         },
8485
8486         /**
8487          * Removes worthless text nodes
8488          * @param {Boolean} forceReclean (optional) By default the element
8489          * keeps track if it has been cleaned already so
8490          * you can call this over and over. However, if you update the element and
8491          * need to force a reclean, you can pass true.
8492          */
8493         clean : function(forceReclean){
8494             if(this.isCleaned && forceReclean !== true){
8495                 return this;
8496             }
8497             var ns = /\S/;
8498             var d = this.dom, n = d.firstChild, ni = -1;
8499             while(n){
8500                 var nx = n.nextSibling;
8501                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8502                     d.removeChild(n);
8503                 }else{
8504                     n.nodeIndex = ++ni;
8505                 }
8506                 n = nx;
8507             }
8508             this.isCleaned = true;
8509             return this;
8510         },
8511
8512         // private
8513         calcOffsetsTo : function(el){
8514             el = Roo.get(el);
8515             var d = el.dom;
8516             var restorePos = false;
8517             if(el.getStyle('position') == 'static'){
8518                 el.position('relative');
8519                 restorePos = true;
8520             }
8521             var x = 0, y =0;
8522             var op = this.dom;
8523             while(op && op != d && op.tagName != 'HTML'){
8524                 x+= op.offsetLeft;
8525                 y+= op.offsetTop;
8526                 op = op.offsetParent;
8527             }
8528             if(restorePos){
8529                 el.position('static');
8530             }
8531             return [x, y];
8532         },
8533
8534         /**
8535          * Scrolls this element into view within the passed container.
8536          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8537          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8538          * @return {Roo.Element} this
8539          */
8540         scrollIntoView : function(container, hscroll){
8541             var c = Roo.getDom(container) || document.body;
8542             var el = this.dom;
8543
8544             var o = this.calcOffsetsTo(c),
8545                 l = o[0],
8546                 t = o[1],
8547                 b = t+el.offsetHeight,
8548                 r = l+el.offsetWidth;
8549
8550             var ch = c.clientHeight;
8551             var ct = parseInt(c.scrollTop, 10);
8552             var cl = parseInt(c.scrollLeft, 10);
8553             var cb = ct + ch;
8554             var cr = cl + c.clientWidth;
8555
8556             if(t < ct){
8557                 c.scrollTop = t;
8558             }else if(b > cb){
8559                 c.scrollTop = b-ch;
8560             }
8561
8562             if(hscroll !== false){
8563                 if(l < cl){
8564                     c.scrollLeft = l;
8565                 }else if(r > cr){
8566                     c.scrollLeft = r-c.clientWidth;
8567                 }
8568             }
8569             return this;
8570         },
8571
8572         // private
8573         scrollChildIntoView : function(child, hscroll){
8574             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8575         },
8576
8577         /**
8578          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8579          * the new height may not be available immediately.
8580          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8581          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8582          * @param {Function} onComplete (optional) Function to call when animation completes
8583          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8584          * @return {Roo.Element} this
8585          */
8586         autoHeight : function(animate, duration, onComplete, easing){
8587             var oldHeight = this.getHeight();
8588             this.clip();
8589             this.setHeight(1); // force clipping
8590             setTimeout(function(){
8591                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8592                 if(!animate){
8593                     this.setHeight(height);
8594                     this.unclip();
8595                     if(typeof onComplete == "function"){
8596                         onComplete();
8597                     }
8598                 }else{
8599                     this.setHeight(oldHeight); // restore original height
8600                     this.setHeight(height, animate, duration, function(){
8601                         this.unclip();
8602                         if(typeof onComplete == "function") { onComplete(); }
8603                     }.createDelegate(this), easing);
8604                 }
8605             }.createDelegate(this), 0);
8606             return this;
8607         },
8608
8609         /**
8610          * Returns true if this element is an ancestor of the passed element
8611          * @param {HTMLElement/String} el The element to check
8612          * @return {Boolean} True if this element is an ancestor of el, else false
8613          */
8614         contains : function(el){
8615             if(!el){return false;}
8616             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8617         },
8618
8619         /**
8620          * Checks whether the element is currently visible using both visibility and display properties.
8621          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8622          * @return {Boolean} True if the element is currently visible, else false
8623          */
8624         isVisible : function(deep) {
8625             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8626             if(deep !== true || !vis){
8627                 return vis;
8628             }
8629             var p = this.dom.parentNode;
8630             while(p && p.tagName.toLowerCase() != "body"){
8631                 if(!Roo.fly(p, '_isVisible').isVisible()){
8632                     return false;
8633                 }
8634                 p = p.parentNode;
8635             }
8636             return true;
8637         },
8638
8639         /**
8640          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8641          * @param {String} selector The CSS selector
8642          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8643          * @return {CompositeElement/CompositeElementLite} The composite element
8644          */
8645         select : function(selector, unique){
8646             return El.select(selector, unique, this.dom);
8647         },
8648
8649         /**
8650          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8651          * @param {String} selector The CSS selector
8652          * @return {Array} An array of the matched nodes
8653          */
8654         query : function(selector, unique){
8655             return Roo.DomQuery.select(selector, this.dom);
8656         },
8657
8658         /**
8659          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8660          * @param {String} selector The CSS selector
8661          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8662          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8663          */
8664         child : function(selector, returnDom){
8665             var n = Roo.DomQuery.selectNode(selector, this.dom);
8666             return returnDom ? n : Roo.get(n);
8667         },
8668
8669         /**
8670          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8671          * @param {String} selector The CSS selector
8672          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8673          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8674          */
8675         down : function(selector, returnDom){
8676             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8677             return returnDom ? n : Roo.get(n);
8678         },
8679
8680         /**
8681          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8682          * @param {String} group The group the DD object is member of
8683          * @param {Object} config The DD config object
8684          * @param {Object} overrides An object containing methods to override/implement on the DD object
8685          * @return {Roo.dd.DD} The DD object
8686          */
8687         initDD : function(group, config, overrides){
8688             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8689             return Roo.apply(dd, overrides);
8690         },
8691
8692         /**
8693          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8694          * @param {String} group The group the DDProxy object is member of
8695          * @param {Object} config The DDProxy config object
8696          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8697          * @return {Roo.dd.DDProxy} The DDProxy object
8698          */
8699         initDDProxy : function(group, config, overrides){
8700             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8701             return Roo.apply(dd, overrides);
8702         },
8703
8704         /**
8705          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8706          * @param {String} group The group the DDTarget object is member of
8707          * @param {Object} config The DDTarget config object
8708          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8709          * @return {Roo.dd.DDTarget} The DDTarget object
8710          */
8711         initDDTarget : function(group, config, overrides){
8712             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8713             return Roo.apply(dd, overrides);
8714         },
8715
8716         /**
8717          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8718          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8719          * @param {Boolean} visible Whether the element is visible
8720          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8721          * @return {Roo.Element} this
8722          */
8723          setVisible : function(visible, animate){
8724             if(!animate || !A){
8725                 if(this.visibilityMode == El.DISPLAY){
8726                     this.setDisplayed(visible);
8727                 }else{
8728                     this.fixDisplay();
8729                     this.dom.style.visibility = visible ? "visible" : "hidden";
8730                 }
8731             }else{
8732                 // closure for composites
8733                 var dom = this.dom;
8734                 var visMode = this.visibilityMode;
8735                 if(visible){
8736                     this.setOpacity(.01);
8737                     this.setVisible(true);
8738                 }
8739                 this.anim({opacity: { to: (visible?1:0) }},
8740                       this.preanim(arguments, 1),
8741                       null, .35, 'easeIn', function(){
8742                          if(!visible){
8743                              if(visMode == El.DISPLAY){
8744                                  dom.style.display = "none";
8745                              }else{
8746                                  dom.style.visibility = "hidden";
8747                              }
8748                              Roo.get(dom).setOpacity(1);
8749                          }
8750                      });
8751             }
8752             return this;
8753         },
8754
8755         /**
8756          * Returns true if display is not "none"
8757          * @return {Boolean}
8758          */
8759         isDisplayed : function() {
8760             return this.getStyle("display") != "none";
8761         },
8762
8763         /**
8764          * Toggles the element's visibility or display, depending on visibility mode.
8765          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8766          * @return {Roo.Element} this
8767          */
8768         toggle : function(animate){
8769             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8770             return this;
8771         },
8772
8773         /**
8774          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8775          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8776          * @return {Roo.Element} this
8777          */
8778         setDisplayed : function(value) {
8779             if(typeof value == "boolean"){
8780                value = value ? this.originalDisplay : "none";
8781             }
8782             this.setStyle("display", value);
8783             return this;
8784         },
8785
8786         /**
8787          * Tries to focus the element. Any exceptions are caught and ignored.
8788          * @return {Roo.Element} this
8789          */
8790         focus : function() {
8791             try{
8792                 this.dom.focus();
8793             }catch(e){}
8794             return this;
8795         },
8796
8797         /**
8798          * Tries to blur the element. Any exceptions are caught and ignored.
8799          * @return {Roo.Element} this
8800          */
8801         blur : function() {
8802             try{
8803                 this.dom.blur();
8804             }catch(e){}
8805             return this;
8806         },
8807
8808         /**
8809          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8810          * @param {String/Array} className The CSS class to add, or an array of classes
8811          * @return {Roo.Element} this
8812          */
8813         addClass : function(className){
8814             if(className instanceof Array){
8815                 for(var i = 0, len = className.length; i < len; i++) {
8816                     this.addClass(className[i]);
8817                 }
8818             }else{
8819                 if(className && !this.hasClass(className)){
8820                     if (this.dom instanceof SVGElement) {
8821                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8822                     } else {
8823                         this.dom.className = this.dom.className + " " + className;
8824                     }
8825                 }
8826             }
8827             return this;
8828         },
8829
8830         /**
8831          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8832          * @param {String/Array} className The CSS class to add, or an array of classes
8833          * @return {Roo.Element} this
8834          */
8835         radioClass : function(className){
8836             var siblings = this.dom.parentNode.childNodes;
8837             for(var i = 0; i < siblings.length; i++) {
8838                 var s = siblings[i];
8839                 if(s.nodeType == 1){
8840                     Roo.get(s).removeClass(className);
8841                 }
8842             }
8843             this.addClass(className);
8844             return this;
8845         },
8846
8847         /**
8848          * Removes one or more CSS classes from the element.
8849          * @param {String/Array} className The CSS class to remove, or an array of classes
8850          * @return {Roo.Element} this
8851          */
8852         removeClass : function(className){
8853             
8854             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8855             if(!className || !cn){
8856                 return this;
8857             }
8858             if(className instanceof Array){
8859                 for(var i = 0, len = className.length; i < len; i++) {
8860                     this.removeClass(className[i]);
8861                 }
8862             }else{
8863                 if(this.hasClass(className)){
8864                     var re = this.classReCache[className];
8865                     if (!re) {
8866                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8867                        this.classReCache[className] = re;
8868                     }
8869                     if (this.dom instanceof SVGElement) {
8870                         this.dom.className.baseVal = cn.replace(re, " ");
8871                     } else {
8872                         this.dom.className = cn.replace(re, " ");
8873                     }
8874                 }
8875             }
8876             return this;
8877         },
8878
8879         // private
8880         classReCache: {},
8881
8882         /**
8883          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8884          * @param {String} className The CSS class to toggle
8885          * @return {Roo.Element} this
8886          */
8887         toggleClass : function(className){
8888             if(this.hasClass(className)){
8889                 this.removeClass(className);
8890             }else{
8891                 this.addClass(className);
8892             }
8893             return this;
8894         },
8895
8896         /**
8897          * Checks if the specified CSS class exists on this element's DOM node.
8898          * @param {String} className The CSS class to check for
8899          * @return {Boolean} True if the class exists, else false
8900          */
8901         hasClass : function(className){
8902             if (this.dom instanceof SVGElement) {
8903                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8904             } 
8905             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8906         },
8907
8908         /**
8909          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8910          * @param {String} oldClassName The CSS class to replace
8911          * @param {String} newClassName The replacement CSS class
8912          * @return {Roo.Element} this
8913          */
8914         replaceClass : function(oldClassName, newClassName){
8915             this.removeClass(oldClassName);
8916             this.addClass(newClassName);
8917             return this;
8918         },
8919
8920         /**
8921          * Returns an object with properties matching the styles requested.
8922          * For example, el.getStyles('color', 'font-size', 'width') might return
8923          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8924          * @param {String} style1 A style name
8925          * @param {String} style2 A style name
8926          * @param {String} etc.
8927          * @return {Object} The style object
8928          */
8929         getStyles : function(){
8930             var a = arguments, len = a.length, r = {};
8931             for(var i = 0; i < len; i++){
8932                 r[a[i]] = this.getStyle(a[i]);
8933             }
8934             return r;
8935         },
8936
8937         /**
8938          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8939          * @param {String} property The style property whose value is returned.
8940          * @return {String} The current value of the style property for this element.
8941          */
8942         getStyle : function(){
8943             return view && view.getComputedStyle ?
8944                 function(prop){
8945                     var el = this.dom, v, cs, camel;
8946                     if(prop == 'float'){
8947                         prop = "cssFloat";
8948                     }
8949                     if(el.style && (v = el.style[prop])){
8950                         return v;
8951                     }
8952                     if(cs = view.getComputedStyle(el, "")){
8953                         if(!(camel = propCache[prop])){
8954                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8955                         }
8956                         return cs[camel];
8957                     }
8958                     return null;
8959                 } :
8960                 function(prop){
8961                     var el = this.dom, v, cs, camel;
8962                     if(prop == 'opacity'){
8963                         if(typeof el.style.filter == 'string'){
8964                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8965                             if(m){
8966                                 var fv = parseFloat(m[1]);
8967                                 if(!isNaN(fv)){
8968                                     return fv ? fv / 100 : 0;
8969                                 }
8970                             }
8971                         }
8972                         return 1;
8973                     }else if(prop == 'float'){
8974                         prop = "styleFloat";
8975                     }
8976                     if(!(camel = propCache[prop])){
8977                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8978                     }
8979                     if(v = el.style[camel]){
8980                         return v;
8981                     }
8982                     if(cs = el.currentStyle){
8983                         return cs[camel];
8984                     }
8985                     return null;
8986                 };
8987         }(),
8988
8989         /**
8990          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8991          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8992          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8993          * @return {Roo.Element} this
8994          */
8995         setStyle : function(prop, value){
8996             if(typeof prop == "string"){
8997                 
8998                 if (prop == 'float') {
8999                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
9000                     return this;
9001                 }
9002                 
9003                 var camel;
9004                 if(!(camel = propCache[prop])){
9005                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
9006                 }
9007                 
9008                 if(camel == 'opacity') {
9009                     this.setOpacity(value);
9010                 }else{
9011                     this.dom.style[camel] = value;
9012                 }
9013             }else{
9014                 for(var style in prop){
9015                     if(typeof prop[style] != "function"){
9016                        this.setStyle(style, prop[style]);
9017                     }
9018                 }
9019             }
9020             return this;
9021         },
9022
9023         /**
9024          * More flexible version of {@link #setStyle} for setting style properties.
9025          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
9026          * a function which returns such a specification.
9027          * @return {Roo.Element} this
9028          */
9029         applyStyles : function(style){
9030             Roo.DomHelper.applyStyles(this.dom, style);
9031             return this;
9032         },
9033
9034         /**
9035           * 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).
9036           * @return {Number} The X position of the element
9037           */
9038         getX : function(){
9039             return D.getX(this.dom);
9040         },
9041
9042         /**
9043           * 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).
9044           * @return {Number} The Y position of the element
9045           */
9046         getY : function(){
9047             return D.getY(this.dom);
9048         },
9049
9050         /**
9051           * 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).
9052           * @return {Array} The XY position of the element
9053           */
9054         getXY : function(){
9055             return D.getXY(this.dom);
9056         },
9057
9058         /**
9059          * 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).
9060          * @param {Number} The X position of the element
9061          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9062          * @return {Roo.Element} this
9063          */
9064         setX : function(x, animate){
9065             if(!animate || !A){
9066                 D.setX(this.dom, x);
9067             }else{
9068                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
9069             }
9070             return this;
9071         },
9072
9073         /**
9074          * 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).
9075          * @param {Number} The Y position of the element
9076          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9077          * @return {Roo.Element} this
9078          */
9079         setY : function(y, animate){
9080             if(!animate || !A){
9081                 D.setY(this.dom, y);
9082             }else{
9083                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
9084             }
9085             return this;
9086         },
9087
9088         /**
9089          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
9090          * @param {String} left The left CSS property value
9091          * @return {Roo.Element} this
9092          */
9093         setLeft : function(left){
9094             this.setStyle("left", this.addUnits(left));
9095             return this;
9096         },
9097
9098         /**
9099          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
9100          * @param {String} top The top CSS property value
9101          * @return {Roo.Element} this
9102          */
9103         setTop : function(top){
9104             this.setStyle("top", this.addUnits(top));
9105             return this;
9106         },
9107
9108         /**
9109          * Sets the element's CSS right style.
9110          * @param {String} right The right CSS property value
9111          * @return {Roo.Element} this
9112          */
9113         setRight : function(right){
9114             this.setStyle("right", this.addUnits(right));
9115             return this;
9116         },
9117
9118         /**
9119          * Sets the element's CSS bottom style.
9120          * @param {String} bottom The bottom CSS property value
9121          * @return {Roo.Element} this
9122          */
9123         setBottom : function(bottom){
9124             this.setStyle("bottom", this.addUnits(bottom));
9125             return this;
9126         },
9127
9128         /**
9129          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9130          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9131          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
9132          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9133          * @return {Roo.Element} this
9134          */
9135         setXY : function(pos, animate){
9136             if(!animate || !A){
9137                 D.setXY(this.dom, pos);
9138             }else{
9139                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
9140             }
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 {Number} x X value for new position (coordinates are page-based)
9148          * @param {Number} y Y value for new position (coordinates are page-based)
9149          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9150          * @return {Roo.Element} this
9151          */
9152         setLocation : function(x, y, animate){
9153             this.setXY([x, y], this.preanim(arguments, 2));
9154             return this;
9155         },
9156
9157         /**
9158          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
9159          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
9160          * @param {Number} x X value for new position (coordinates are page-based)
9161          * @param {Number} y Y value for new position (coordinates are page-based)
9162          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
9163          * @return {Roo.Element} this
9164          */
9165         moveTo : function(x, y, animate){
9166             this.setXY([x, y], this.preanim(arguments, 2));
9167             return this;
9168         },
9169
9170         /**
9171          * Returns the region of the given element.
9172          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
9173          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
9174          */
9175         getRegion : function(){
9176             return D.getRegion(this.dom);
9177         },
9178
9179         /**
9180          * Returns the offset height of the element
9181          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
9182          * @return {Number} The element's height
9183          */
9184         getHeight : function(contentHeight){
9185             var h = this.dom.offsetHeight || 0;
9186             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
9187         },
9188
9189         /**
9190          * Returns the offset width of the element
9191          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
9192          * @return {Number} The element's width
9193          */
9194         getWidth : function(contentWidth){
9195             var w = this.dom.offsetWidth || 0;
9196             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
9197         },
9198
9199         /**
9200          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
9201          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
9202          * if a height has not been set using CSS.
9203          * @return {Number}
9204          */
9205         getComputedHeight : function(){
9206             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
9207             if(!h){
9208                 h = parseInt(this.getStyle('height'), 10) || 0;
9209                 if(!this.isBorderBox()){
9210                     h += this.getFrameWidth('tb');
9211                 }
9212             }
9213             return h;
9214         },
9215
9216         /**
9217          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
9218          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
9219          * if a width has not been set using CSS.
9220          * @return {Number}
9221          */
9222         getComputedWidth : function(){
9223             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
9224             if(!w){
9225                 w = parseInt(this.getStyle('width'), 10) || 0;
9226                 if(!this.isBorderBox()){
9227                     w += this.getFrameWidth('lr');
9228                 }
9229             }
9230             return w;
9231         },
9232
9233         /**
9234          * Returns the size of the element.
9235          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
9236          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
9237          */
9238         getSize : function(contentSize){
9239             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
9240         },
9241
9242         /**
9243          * Returns the width and height of the viewport.
9244          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
9245          */
9246         getViewSize : function(){
9247             var d = this.dom, doc = document, aw = 0, ah = 0;
9248             if(d == doc || d == doc.body){
9249                 return {width : D.getViewWidth(), height: D.getViewHeight()};
9250             }else{
9251                 return {
9252                     width : d.clientWidth,
9253                     height: d.clientHeight
9254                 };
9255             }
9256         },
9257
9258         /**
9259          * Returns the value of the "value" attribute
9260          * @param {Boolean} asNumber true to parse the value as a number
9261          * @return {String/Number}
9262          */
9263         getValue : function(asNumber){
9264             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
9265         },
9266
9267         // private
9268         adjustWidth : function(width){
9269             if(typeof width == "number"){
9270                 if(this.autoBoxAdjust && !this.isBorderBox()){
9271                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9272                 }
9273                 if(width < 0){
9274                     width = 0;
9275                 }
9276             }
9277             return width;
9278         },
9279
9280         // private
9281         adjustHeight : function(height){
9282             if(typeof height == "number"){
9283                if(this.autoBoxAdjust && !this.isBorderBox()){
9284                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9285                }
9286                if(height < 0){
9287                    height = 0;
9288                }
9289             }
9290             return height;
9291         },
9292
9293         /**
9294          * Set the width of the element
9295          * @param {Number} width The new width
9296          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9297          * @return {Roo.Element} this
9298          */
9299         setWidth : function(width, animate){
9300             width = this.adjustWidth(width);
9301             if(!animate || !A){
9302                 this.dom.style.width = this.addUnits(width);
9303             }else{
9304                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
9305             }
9306             return this;
9307         },
9308
9309         /**
9310          * Set the height of the element
9311          * @param {Number} height The new height
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          setHeight : function(height, animate){
9316             height = this.adjustHeight(height);
9317             if(!animate || !A){
9318                 this.dom.style.height = this.addUnits(height);
9319             }else{
9320                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9321             }
9322             return this;
9323         },
9324
9325         /**
9326          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9327          * @param {Number} width The new width
9328          * @param {Number} height The new height
9329          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9330          * @return {Roo.Element} this
9331          */
9332          setSize : function(width, height, animate){
9333             if(typeof width == "object"){ // in case of object from getSize()
9334                 height = width.height; width = width.width;
9335             }
9336             width = this.adjustWidth(width); height = this.adjustHeight(height);
9337             if(!animate || !A){
9338                 this.dom.style.width = this.addUnits(width);
9339                 this.dom.style.height = this.addUnits(height);
9340             }else{
9341                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9342             }
9343             return this;
9344         },
9345
9346         /**
9347          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9348          * @param {Number} x X value for new position (coordinates are page-based)
9349          * @param {Number} y Y value for new position (coordinates are page-based)
9350          * @param {Number} width The new width
9351          * @param {Number} height The new height
9352          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9353          * @return {Roo.Element} this
9354          */
9355         setBounds : function(x, y, width, height, animate){
9356             if(!animate || !A){
9357                 this.setSize(width, height);
9358                 this.setLocation(x, y);
9359             }else{
9360                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9361                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9362                               this.preanim(arguments, 4), 'motion');
9363             }
9364             return this;
9365         },
9366
9367         /**
9368          * 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.
9369          * @param {Roo.lib.Region} region The region to fill
9370          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9371          * @return {Roo.Element} this
9372          */
9373         setRegion : function(region, animate){
9374             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9375             return this;
9376         },
9377
9378         /**
9379          * Appends an event handler
9380          *
9381          * @param {String}   eventName     The type of event to append
9382          * @param {Function} fn        The method the event invokes
9383          * @param {Object} scope       (optional) The scope (this object) of the fn
9384          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9385          */
9386         addListener : function(eventName, fn, scope, options)
9387         {
9388             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9389                 this.addListener('touchstart', this.onTapHandler, this);
9390             }
9391             
9392             // we need to handle a special case where dom element is a svg element.
9393             // in this case we do not actua
9394             if (!this.dom) {
9395                 return;
9396             }
9397             
9398             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9399                 if (typeof(this.listeners[eventName]) == 'undefined') {
9400                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9401                 }
9402                 this.listeners[eventName].addListener(fn, scope, options);
9403                 return;
9404             }
9405             
9406                 
9407             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9408             
9409             
9410         },
9411         tapedTwice : false,
9412         onTapHandler : function(event)
9413         {
9414             if(!this.tapedTwice) {
9415                 this.tapedTwice = true;
9416                 var s = this;
9417                 setTimeout( function() {
9418                     s.tapedTwice = false;
9419                 }, 300 );
9420                 return;
9421             }
9422             event.preventDefault();
9423             var revent = new MouseEvent('dblclick',  {
9424                 view: window,
9425                 bubbles: true,
9426                 cancelable: true
9427             });
9428              
9429             this.dom.dispatchEvent(revent);
9430             //action on double tap goes below
9431              
9432         }, 
9433  
9434         /**
9435          * Removes an event handler from this element
9436          * @param {String} eventName the type of event to remove
9437          * @param {Function} fn the method the event invokes
9438          * @param {Function} scope (needed for svg fake listeners)
9439          * @return {Roo.Element} this
9440          */
9441         removeListener : function(eventName, fn, scope){
9442             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9443             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9444                 return this;
9445             }
9446             this.listeners[eventName].removeListener(fn, scope);
9447             return this;
9448         },
9449
9450         /**
9451          * Removes all previous added listeners from this element
9452          * @return {Roo.Element} this
9453          */
9454         removeAllListeners : function(){
9455             E.purgeElement(this.dom);
9456             this.listeners = {};
9457             return this;
9458         },
9459
9460         relayEvent : function(eventName, observable){
9461             this.on(eventName, function(e){
9462                 observable.fireEvent(eventName, e);
9463             });
9464         },
9465
9466         
9467         /**
9468          * Set the opacity of the element
9469          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9470          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9471          * @return {Roo.Element} this
9472          */
9473          setOpacity : function(opacity, animate){
9474             if(!animate || !A){
9475                 var s = this.dom.style;
9476                 if(Roo.isIE){
9477                     s.zoom = 1;
9478                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9479                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9480                 }else{
9481                     s.opacity = opacity;
9482                 }
9483             }else{
9484                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9485             }
9486             return this;
9487         },
9488
9489         /**
9490          * Gets the left X coordinate
9491          * @param {Boolean} local True to get the local css position instead of page coordinate
9492          * @return {Number}
9493          */
9494         getLeft : function(local){
9495             if(!local){
9496                 return this.getX();
9497             }else{
9498                 return parseInt(this.getStyle("left"), 10) || 0;
9499             }
9500         },
9501
9502         /**
9503          * Gets the right X coordinate of the element (element X position + element width)
9504          * @param {Boolean} local True to get the local css position instead of page coordinate
9505          * @return {Number}
9506          */
9507         getRight : function(local){
9508             if(!local){
9509                 return this.getX() + this.getWidth();
9510             }else{
9511                 return (this.getLeft(true) + this.getWidth()) || 0;
9512             }
9513         },
9514
9515         /**
9516          * Gets the top Y coordinate
9517          * @param {Boolean} local True to get the local css position instead of page coordinate
9518          * @return {Number}
9519          */
9520         getTop : function(local) {
9521             if(!local){
9522                 return this.getY();
9523             }else{
9524                 return parseInt(this.getStyle("top"), 10) || 0;
9525             }
9526         },
9527
9528         /**
9529          * Gets the bottom Y coordinate of the element (element Y position + element height)
9530          * @param {Boolean} local True to get the local css position instead of page coordinate
9531          * @return {Number}
9532          */
9533         getBottom : function(local){
9534             if(!local){
9535                 return this.getY() + this.getHeight();
9536             }else{
9537                 return (this.getTop(true) + this.getHeight()) || 0;
9538             }
9539         },
9540
9541         /**
9542         * Initializes positioning on this element. If a desired position is not passed, it will make the
9543         * the element positioned relative IF it is not already positioned.
9544         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9545         * @param {Number} zIndex (optional) The zIndex to apply
9546         * @param {Number} x (optional) Set the page X position
9547         * @param {Number} y (optional) Set the page Y position
9548         */
9549         position : function(pos, zIndex, x, y){
9550             if(!pos){
9551                if(this.getStyle('position') == 'static'){
9552                    this.setStyle('position', 'relative');
9553                }
9554             }else{
9555                 this.setStyle("position", pos);
9556             }
9557             if(zIndex){
9558                 this.setStyle("z-index", zIndex);
9559             }
9560             if(x !== undefined && y !== undefined){
9561                 this.setXY([x, y]);
9562             }else if(x !== undefined){
9563                 this.setX(x);
9564             }else if(y !== undefined){
9565                 this.setY(y);
9566             }
9567         },
9568
9569         /**
9570         * Clear positioning back to the default when the document was loaded
9571         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9572         * @return {Roo.Element} this
9573          */
9574         clearPositioning : function(value){
9575             value = value ||'';
9576             this.setStyle({
9577                 "left": value,
9578                 "right": value,
9579                 "top": value,
9580                 "bottom": value,
9581                 "z-index": "",
9582                 "position" : "static"
9583             });
9584             return this;
9585         },
9586
9587         /**
9588         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9589         * snapshot before performing an update and then restoring the element.
9590         * @return {Object}
9591         */
9592         getPositioning : function(){
9593             var l = this.getStyle("left");
9594             var t = this.getStyle("top");
9595             return {
9596                 "position" : this.getStyle("position"),
9597                 "left" : l,
9598                 "right" : l ? "" : this.getStyle("right"),
9599                 "top" : t,
9600                 "bottom" : t ? "" : this.getStyle("bottom"),
9601                 "z-index" : this.getStyle("z-index")
9602             };
9603         },
9604
9605         /**
9606          * Gets the width of the border(s) for the specified side(s)
9607          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9608          * passing lr would get the border (l)eft width + the border (r)ight width.
9609          * @return {Number} The width of the sides passed added together
9610          */
9611         getBorderWidth : function(side){
9612             return this.addStyles(side, El.borders);
9613         },
9614
9615         /**
9616          * Gets the width of the padding(s) for the specified side(s)
9617          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9618          * passing lr would get the padding (l)eft + the padding (r)ight.
9619          * @return {Number} The padding of the sides passed added together
9620          */
9621         getPadding : function(side){
9622             return this.addStyles(side, El.paddings);
9623         },
9624
9625         /**
9626         * Set positioning with an object returned by getPositioning().
9627         * @param {Object} posCfg
9628         * @return {Roo.Element} this
9629          */
9630         setPositioning : function(pc){
9631             this.applyStyles(pc);
9632             if(pc.right == "auto"){
9633                 this.dom.style.right = "";
9634             }
9635             if(pc.bottom == "auto"){
9636                 this.dom.style.bottom = "";
9637             }
9638             return this;
9639         },
9640
9641         // private
9642         fixDisplay : function(){
9643             if(this.getStyle("display") == "none"){
9644                 this.setStyle("visibility", "hidden");
9645                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9646                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9647                     this.setStyle("display", "block");
9648                 }
9649             }
9650         },
9651
9652         /**
9653          * Quick set left and top adding default units
9654          * @param {String} left The left CSS property value
9655          * @param {String} top The top CSS property value
9656          * @return {Roo.Element} this
9657          */
9658          setLeftTop : function(left, top){
9659             this.dom.style.left = this.addUnits(left);
9660             this.dom.style.top = this.addUnits(top);
9661             return this;
9662         },
9663
9664         /**
9665          * Move this element relative to its current position.
9666          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9667          * @param {Number} distance How far to move the element in pixels
9668          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9669          * @return {Roo.Element} this
9670          */
9671          move : function(direction, distance, animate){
9672             var xy = this.getXY();
9673             direction = direction.toLowerCase();
9674             switch(direction){
9675                 case "l":
9676                 case "left":
9677                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9678                     break;
9679                case "r":
9680                case "right":
9681                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9682                     break;
9683                case "t":
9684                case "top":
9685                case "up":
9686                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9687                     break;
9688                case "b":
9689                case "bottom":
9690                case "down":
9691                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9692                     break;
9693             }
9694             return this;
9695         },
9696
9697         /**
9698          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9699          * @return {Roo.Element} this
9700          */
9701         clip : function(){
9702             if(!this.isClipped){
9703                this.isClipped = true;
9704                this.originalClip = {
9705                    "o": this.getStyle("overflow"),
9706                    "x": this.getStyle("overflow-x"),
9707                    "y": this.getStyle("overflow-y")
9708                };
9709                this.setStyle("overflow", "hidden");
9710                this.setStyle("overflow-x", "hidden");
9711                this.setStyle("overflow-y", "hidden");
9712             }
9713             return this;
9714         },
9715
9716         /**
9717          *  Return clipping (overflow) to original clipping before clip() was called
9718          * @return {Roo.Element} this
9719          */
9720         unclip : function(){
9721             if(this.isClipped){
9722                 this.isClipped = false;
9723                 var o = this.originalClip;
9724                 if(o.o){this.setStyle("overflow", o.o);}
9725                 if(o.x){this.setStyle("overflow-x", o.x);}
9726                 if(o.y){this.setStyle("overflow-y", o.y);}
9727             }
9728             return this;
9729         },
9730
9731
9732         /**
9733          * Gets the x,y coordinates specified by the anchor position on the element.
9734          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9735          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9736          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9737          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9738          * @return {Array} [x, y] An array containing the element's x and y coordinates
9739          */
9740         getAnchorXY : function(anchor, local, s){
9741             //Passing a different size is useful for pre-calculating anchors,
9742             //especially for anchored animations that change the el size.
9743
9744             var w, h, vp = false;
9745             if(!s){
9746                 var d = this.dom;
9747                 if(d == document.body || d == document){
9748                     vp = true;
9749                     w = D.getViewWidth(); h = D.getViewHeight();
9750                 }else{
9751                     w = this.getWidth(); h = this.getHeight();
9752                 }
9753             }else{
9754                 w = s.width;  h = s.height;
9755             }
9756             var x = 0, y = 0, r = Math.round;
9757             switch((anchor || "tl").toLowerCase()){
9758                 case "c":
9759                     x = r(w*.5);
9760                     y = r(h*.5);
9761                 break;
9762                 case "t":
9763                     x = r(w*.5);
9764                     y = 0;
9765                 break;
9766                 case "l":
9767                     x = 0;
9768                     y = r(h*.5);
9769                 break;
9770                 case "r":
9771                     x = w;
9772                     y = r(h*.5);
9773                 break;
9774                 case "b":
9775                     x = r(w*.5);
9776                     y = h;
9777                 break;
9778                 case "tl":
9779                     x = 0;
9780                     y = 0;
9781                 break;
9782                 case "bl":
9783                     x = 0;
9784                     y = h;
9785                 break;
9786                 case "br":
9787                     x = w;
9788                     y = h;
9789                 break;
9790                 case "tr":
9791                     x = w;
9792                     y = 0;
9793                 break;
9794             }
9795             if(local === true){
9796                 return [x, y];
9797             }
9798             if(vp){
9799                 var sc = this.getScroll();
9800                 return [x + sc.left, y + sc.top];
9801             }
9802             //Add the element's offset xy
9803             var o = this.getXY();
9804             return [x+o[0], y+o[1]];
9805         },
9806
9807         /**
9808          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9809          * supported position values.
9810          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9811          * @param {String} position The position to align to.
9812          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9813          * @return {Array} [x, y]
9814          */
9815         getAlignToXY : function(el, p, o)
9816         {
9817             el = Roo.get(el);
9818             var d = this.dom;
9819             if(!el.dom){
9820                 throw "Element.alignTo with an element that doesn't exist";
9821             }
9822             var c = false; //constrain to viewport
9823             var p1 = "", p2 = "";
9824             o = o || [0,0];
9825
9826             if(!p){
9827                 p = "tl-bl";
9828             }else if(p == "?"){
9829                 p = "tl-bl?";
9830             }else if(p.indexOf("-") == -1){
9831                 p = "tl-" + p;
9832             }
9833             p = p.toLowerCase();
9834             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9835             if(!m){
9836                throw "Element.alignTo with an invalid alignment " + p;
9837             }
9838             p1 = m[1]; p2 = m[2]; c = !!m[3];
9839
9840             //Subtract the aligned el's internal xy from the target's offset xy
9841             //plus custom offset to get the aligned el's new offset xy
9842             var a1 = this.getAnchorXY(p1, true);
9843             var a2 = el.getAnchorXY(p2, false);
9844             var x = a2[0] - a1[0] + o[0];
9845             var y = a2[1] - a1[1] + o[1];
9846             if(c){
9847                 //constrain the aligned el to viewport if necessary
9848                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9849                 // 5px of margin for ie
9850                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9851
9852                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9853                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9854                 //otherwise swap the aligned el to the opposite border of the target.
9855                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9856                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9857                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9858                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9859
9860                var doc = document;
9861                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9862                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9863
9864                if((x+w) > dw + scrollX){
9865                     x = swapX ? r.left-w : dw+scrollX-w;
9866                 }
9867                if(x < scrollX){
9868                    x = swapX ? r.right : scrollX;
9869                }
9870                if((y+h) > dh + scrollY){
9871                     y = swapY ? r.top-h : dh+scrollY-h;
9872                 }
9873                if (y < scrollY){
9874                    y = swapY ? r.bottom : scrollY;
9875                }
9876             }
9877             return [x,y];
9878         },
9879
9880         // private
9881         getConstrainToXY : function(){
9882             var os = {top:0, left:0, bottom:0, right: 0};
9883
9884             return function(el, local, offsets, proposedXY){
9885                 el = Roo.get(el);
9886                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9887
9888                 var vw, vh, vx = 0, vy = 0;
9889                 if(el.dom == document.body || el.dom == document){
9890                     vw = Roo.lib.Dom.getViewWidth();
9891                     vh = Roo.lib.Dom.getViewHeight();
9892                 }else{
9893                     vw = el.dom.clientWidth;
9894                     vh = el.dom.clientHeight;
9895                     if(!local){
9896                         var vxy = el.getXY();
9897                         vx = vxy[0];
9898                         vy = vxy[1];
9899                     }
9900                 }
9901
9902                 var s = el.getScroll();
9903
9904                 vx += offsets.left + s.left;
9905                 vy += offsets.top + s.top;
9906
9907                 vw -= offsets.right;
9908                 vh -= offsets.bottom;
9909
9910                 var vr = vx+vw;
9911                 var vb = vy+vh;
9912
9913                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9914                 var x = xy[0], y = xy[1];
9915                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9916
9917                 // only move it if it needs it
9918                 var moved = false;
9919
9920                 // first validate right/bottom
9921                 if((x + w) > vr){
9922                     x = vr - w;
9923                     moved = true;
9924                 }
9925                 if((y + h) > vb){
9926                     y = vb - h;
9927                     moved = true;
9928                 }
9929                 // then make sure top/left isn't negative
9930                 if(x < vx){
9931                     x = vx;
9932                     moved = true;
9933                 }
9934                 if(y < vy){
9935                     y = vy;
9936                     moved = true;
9937                 }
9938                 return moved ? [x, y] : false;
9939             };
9940         }(),
9941
9942         // private
9943         adjustForConstraints : function(xy, parent, offsets){
9944             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9945         },
9946
9947         /**
9948          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9949          * document it aligns it to the viewport.
9950          * The position parameter is optional, and can be specified in any one of the following formats:
9951          * <ul>
9952          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9953          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9954          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9955          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9956          *   <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
9957          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9958          * </ul>
9959          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9960          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9961          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9962          * that specified in order to enforce the viewport constraints.
9963          * Following are all of the supported anchor positions:
9964     <pre>
9965     Value  Description
9966     -----  -----------------------------
9967     tl     The top left corner (default)
9968     t      The center of the top edge
9969     tr     The top right corner
9970     l      The center of the left edge
9971     c      In the center of the element
9972     r      The center of the right edge
9973     bl     The bottom left corner
9974     b      The center of the bottom edge
9975     br     The bottom right corner
9976     </pre>
9977     Example Usage:
9978     <pre><code>
9979     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9980     el.alignTo("other-el");
9981
9982     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9983     el.alignTo("other-el", "tr?");
9984
9985     // align the bottom right corner of el with the center left edge of other-el
9986     el.alignTo("other-el", "br-l?");
9987
9988     // align the center of el with the bottom left corner of other-el and
9989     // adjust the x position by -6 pixels (and the y position by 0)
9990     el.alignTo("other-el", "c-bl", [-6, 0]);
9991     </code></pre>
9992          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9993          * @param {String} position The position to align to.
9994          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9995          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9996          * @return {Roo.Element} this
9997          */
9998         alignTo : function(element, position, offsets, animate){
9999             var xy = this.getAlignToXY(element, position, offsets);
10000             this.setXY(xy, this.preanim(arguments, 3));
10001             return this;
10002         },
10003
10004         /**
10005          * Anchors an element to another element and realigns it when the window is resized.
10006          * @param {String/HTMLElement/Roo.Element} element The element to align to.
10007          * @param {String} position The position to align to.
10008          * @param {Array} offsets (optional) Offset the positioning by [x, y]
10009          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
10010          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
10011          * is a number, it is used as the buffer delay (defaults to 50ms).
10012          * @param {Function} callback The function to call after the animation finishes
10013          * @return {Roo.Element} this
10014          */
10015         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
10016             var action = function(){
10017                 this.alignTo(el, alignment, offsets, animate);
10018                 Roo.callback(callback, this);
10019             };
10020             Roo.EventManager.onWindowResize(action, this);
10021             var tm = typeof monitorScroll;
10022             if(tm != 'undefined'){
10023                 Roo.EventManager.on(window, 'scroll', action, this,
10024                     {buffer: tm == 'number' ? monitorScroll : 50});
10025             }
10026             action.call(this); // align immediately
10027             return this;
10028         },
10029         /**
10030          * Clears any opacity settings from this element. Required in some cases for IE.
10031          * @return {Roo.Element} this
10032          */
10033         clearOpacity : function(){
10034             if (window.ActiveXObject) {
10035                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
10036                     this.dom.style.filter = "";
10037                 }
10038             } else {
10039                 this.dom.style.opacity = "";
10040                 this.dom.style["-moz-opacity"] = "";
10041                 this.dom.style["-khtml-opacity"] = "";
10042             }
10043             return this;
10044         },
10045
10046         /**
10047          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10048          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10049          * @return {Roo.Element} this
10050          */
10051         hide : function(animate){
10052             this.setVisible(false, this.preanim(arguments, 0));
10053             return this;
10054         },
10055
10056         /**
10057         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
10058         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10059          * @return {Roo.Element} this
10060          */
10061         show : function(animate){
10062             this.setVisible(true, this.preanim(arguments, 0));
10063             return this;
10064         },
10065
10066         /**
10067          * @private Test if size has a unit, otherwise appends the default
10068          */
10069         addUnits : function(size){
10070             return Roo.Element.addUnits(size, this.defaultUnit);
10071         },
10072
10073         /**
10074          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
10075          * @return {Roo.Element} this
10076          */
10077         beginMeasure : function(){
10078             var el = this.dom;
10079             if(el.offsetWidth || el.offsetHeight){
10080                 return this; // offsets work already
10081             }
10082             var changed = [];
10083             var p = this.dom, b = document.body; // start with this element
10084             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
10085                 var pe = Roo.get(p);
10086                 if(pe.getStyle('display') == 'none'){
10087                     changed.push({el: p, visibility: pe.getStyle("visibility")});
10088                     p.style.visibility = "hidden";
10089                     p.style.display = "block";
10090                 }
10091                 p = p.parentNode;
10092             }
10093             this._measureChanged = changed;
10094             return this;
10095
10096         },
10097
10098         /**
10099          * Restores displays to before beginMeasure was called
10100          * @return {Roo.Element} this
10101          */
10102         endMeasure : function(){
10103             var changed = this._measureChanged;
10104             if(changed){
10105                 for(var i = 0, len = changed.length; i < len; i++) {
10106                     var r = changed[i];
10107                     r.el.style.visibility = r.visibility;
10108                     r.el.style.display = "none";
10109                 }
10110                 this._measureChanged = null;
10111             }
10112             return this;
10113         },
10114
10115         /**
10116         * Update the innerHTML of this element, optionally searching for and processing scripts
10117         * @param {String} html The new HTML
10118         * @param {Boolean} loadScripts (optional) true to look for and process scripts
10119         * @param {Function} callback For async script loading you can be noticed when the update completes
10120         * @return {Roo.Element} this
10121          */
10122         update : function(html, loadScripts, callback){
10123             if(typeof html == "undefined"){
10124                 html = "";
10125             }
10126             if(loadScripts !== true){
10127                 this.dom.innerHTML = html;
10128                 if(typeof callback == "function"){
10129                     callback();
10130                 }
10131                 return this;
10132             }
10133             var id = Roo.id();
10134             var dom = this.dom;
10135
10136             html += '<span id="' + id + '"></span>';
10137
10138             E.onAvailable(id, function(){
10139                 var hd = document.getElementsByTagName("head")[0];
10140                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
10141                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
10142                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
10143
10144                 var match;
10145                 while(match = re.exec(html)){
10146                     var attrs = match[1];
10147                     var srcMatch = attrs ? attrs.match(srcRe) : false;
10148                     if(srcMatch && srcMatch[2]){
10149                        var s = document.createElement("script");
10150                        s.src = srcMatch[2];
10151                        var typeMatch = attrs.match(typeRe);
10152                        if(typeMatch && typeMatch[2]){
10153                            s.type = typeMatch[2];
10154                        }
10155                        hd.appendChild(s);
10156                     }else if(match[2] && match[2].length > 0){
10157                         if(window.execScript) {
10158                            window.execScript(match[2]);
10159                         } else {
10160                             /**
10161                              * eval:var:id
10162                              * eval:var:dom
10163                              * eval:var:html
10164                              * 
10165                              */
10166                            window.eval(match[2]);
10167                         }
10168                     }
10169                 }
10170                 var el = document.getElementById(id);
10171                 if(el){el.parentNode.removeChild(el);}
10172                 if(typeof callback == "function"){
10173                     callback();
10174                 }
10175             });
10176             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
10177             return this;
10178         },
10179
10180         /**
10181          * Direct access to the UpdateManager update() method (takes the same parameters).
10182          * @param {String/Function} url The url for this request or a function to call to get the url
10183          * @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}
10184          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
10185          * @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.
10186          * @return {Roo.Element} this
10187          */
10188         load : function(){
10189             var um = this.getUpdateManager();
10190             um.update.apply(um, arguments);
10191             return this;
10192         },
10193
10194         /**
10195         * Gets this element's UpdateManager
10196         * @return {Roo.UpdateManager} The UpdateManager
10197         */
10198         getUpdateManager : function(){
10199             if(!this.updateManager){
10200                 this.updateManager = new Roo.UpdateManager(this);
10201             }
10202             return this.updateManager;
10203         },
10204
10205         /**
10206          * Disables text selection for this element (normalized across browsers)
10207          * @return {Roo.Element} this
10208          */
10209         unselectable : function(){
10210             this.dom.unselectable = "on";
10211             this.swallowEvent("selectstart", true);
10212             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
10213             this.addClass("x-unselectable");
10214             return this;
10215         },
10216
10217         /**
10218         * Calculates the x, y to center this element on the screen
10219         * @return {Array} The x, y values [x, y]
10220         */
10221         getCenterXY : function(){
10222             return this.getAlignToXY(document, 'c-c');
10223         },
10224
10225         /**
10226         * Centers the Element in either the viewport, or another Element.
10227         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
10228         */
10229         center : function(centerIn){
10230             this.alignTo(centerIn || document, 'c-c');
10231             return this;
10232         },
10233
10234         /**
10235          * Tests various css rules/browsers to determine if this element uses a border box
10236          * @return {Boolean}
10237          */
10238         isBorderBox : function(){
10239             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
10240         },
10241
10242         /**
10243          * Return a box {x, y, width, height} that can be used to set another elements
10244          * size/location to match this element.
10245          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
10246          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
10247          * @return {Object} box An object in the format {x, y, width, height}
10248          */
10249         getBox : function(contentBox, local){
10250             var xy;
10251             if(!local){
10252                 xy = this.getXY();
10253             }else{
10254                 var left = parseInt(this.getStyle("left"), 10) || 0;
10255                 var top = parseInt(this.getStyle("top"), 10) || 0;
10256                 xy = [left, top];
10257             }
10258             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
10259             if(!contentBox){
10260                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
10261             }else{
10262                 var l = this.getBorderWidth("l")+this.getPadding("l");
10263                 var r = this.getBorderWidth("r")+this.getPadding("r");
10264                 var t = this.getBorderWidth("t")+this.getPadding("t");
10265                 var b = this.getBorderWidth("b")+this.getPadding("b");
10266                 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)};
10267             }
10268             bx.right = bx.x + bx.width;
10269             bx.bottom = bx.y + bx.height;
10270             return bx;
10271         },
10272
10273         /**
10274          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
10275          for more information about the sides.
10276          * @param {String} sides
10277          * @return {Number}
10278          */
10279         getFrameWidth : function(sides, onlyContentBox){
10280             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
10281         },
10282
10283         /**
10284          * 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.
10285          * @param {Object} box The box to fill {x, y, width, height}
10286          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
10287          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10288          * @return {Roo.Element} this
10289          */
10290         setBox : function(box, adjust, animate){
10291             var w = box.width, h = box.height;
10292             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
10293                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
10294                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
10295             }
10296             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
10297             return this;
10298         },
10299
10300         /**
10301          * Forces the browser to repaint this element
10302          * @return {Roo.Element} this
10303          */
10304          repaint : function(){
10305             var dom = this.dom;
10306             this.addClass("x-repaint");
10307             setTimeout(function(){
10308                 Roo.get(dom).removeClass("x-repaint");
10309             }, 1);
10310             return this;
10311         },
10312
10313         /**
10314          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
10315          * then it returns the calculated width of the sides (see getPadding)
10316          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
10317          * @return {Object/Number}
10318          */
10319         getMargins : function(side){
10320             if(!side){
10321                 return {
10322                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10323                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10324                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10325                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10326                 };
10327             }else{
10328                 return this.addStyles(side, El.margins);
10329              }
10330         },
10331
10332         // private
10333         addStyles : function(sides, styles){
10334             var val = 0, v, w;
10335             for(var i = 0, len = sides.length; i < len; i++){
10336                 v = this.getStyle(styles[sides.charAt(i)]);
10337                 if(v){
10338                      w = parseInt(v, 10);
10339                      if(w){ val += w; }
10340                 }
10341             }
10342             return val;
10343         },
10344
10345         /**
10346          * Creates a proxy element of this element
10347          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10348          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10349          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10350          * @return {Roo.Element} The new proxy element
10351          */
10352         createProxy : function(config, renderTo, matchBox){
10353             if(renderTo){
10354                 renderTo = Roo.getDom(renderTo);
10355             }else{
10356                 renderTo = document.body;
10357             }
10358             config = typeof config == "object" ?
10359                 config : {tag : "div", cls: config};
10360             var proxy = Roo.DomHelper.append(renderTo, config, true);
10361             if(matchBox){
10362                proxy.setBox(this.getBox());
10363             }
10364             return proxy;
10365         },
10366
10367         /**
10368          * Puts a mask over this element to disable user interaction. Requires core.css.
10369          * This method can only be applied to elements which accept child nodes.
10370          * @param {String} msg (optional) A message to display in the mask
10371          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10372          * @return {Element} The mask  element
10373          */
10374         mask : function(msg, msgCls)
10375         {
10376             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10377                 this.setStyle("position", "relative");
10378             }
10379             if(!this._mask){
10380                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10381             }
10382             
10383             this.addClass("x-masked");
10384             this._mask.setDisplayed(true);
10385             
10386             // we wander
10387             var z = 0;
10388             var dom = this.dom;
10389             while (dom && dom.style) {
10390                 if (!isNaN(parseInt(dom.style.zIndex))) {
10391                     z = Math.max(z, parseInt(dom.style.zIndex));
10392                 }
10393                 dom = dom.parentNode;
10394             }
10395             // if we are masking the body - then it hides everything..
10396             if (this.dom == document.body) {
10397                 z = 1000000;
10398                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10399                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10400             }
10401            
10402             if(typeof msg == 'string'){
10403                 if(!this._maskMsg){
10404                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10405                         cls: "roo-el-mask-msg", 
10406                         cn: [
10407                             {
10408                                 tag: 'i',
10409                                 cls: 'fa fa-spinner fa-spin'
10410                             },
10411                             {
10412                                 tag: 'div'
10413                             }   
10414                         ]
10415                     }, true);
10416                 }
10417                 var mm = this._maskMsg;
10418                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10419                 if (mm.dom.lastChild) { // weird IE issue?
10420                     mm.dom.lastChild.innerHTML = msg;
10421                 }
10422                 mm.setDisplayed(true);
10423                 mm.center(this);
10424                 mm.setStyle('z-index', z + 102);
10425             }
10426             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10427                 this._mask.setHeight(this.getHeight());
10428             }
10429             this._mask.setStyle('z-index', z + 100);
10430             
10431             return this._mask;
10432         },
10433
10434         /**
10435          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10436          * it is cached for reuse.
10437          */
10438         unmask : function(removeEl){
10439             if(this._mask){
10440                 if(removeEl === true){
10441                     this._mask.remove();
10442                     delete this._mask;
10443                     if(this._maskMsg){
10444                         this._maskMsg.remove();
10445                         delete this._maskMsg;
10446                     }
10447                 }else{
10448                     this._mask.setDisplayed(false);
10449                     if(this._maskMsg){
10450                         this._maskMsg.setDisplayed(false);
10451                     }
10452                 }
10453             }
10454             this.removeClass("x-masked");
10455         },
10456
10457         /**
10458          * Returns true if this element is masked
10459          * @return {Boolean}
10460          */
10461         isMasked : function(){
10462             return this._mask && this._mask.isVisible();
10463         },
10464
10465         /**
10466          * Creates an iframe shim for this element to keep selects and other windowed objects from
10467          * showing through.
10468          * @return {Roo.Element} The new shim element
10469          */
10470         createShim : function(){
10471             var el = document.createElement('iframe');
10472             el.frameBorder = 'no';
10473             el.className = 'roo-shim';
10474             if(Roo.isIE && Roo.isSecure){
10475                 el.src = Roo.SSL_SECURE_URL;
10476             }
10477             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10478             shim.autoBoxAdjust = false;
10479             return shim;
10480         },
10481
10482         /**
10483          * Removes this element from the DOM and deletes it from the cache
10484          */
10485         remove : function(){
10486             if(this.dom.parentNode){
10487                 this.dom.parentNode.removeChild(this.dom);
10488             }
10489             delete El.cache[this.dom.id];
10490         },
10491
10492         /**
10493          * Sets up event handlers to add and remove a css class when the mouse is over this element
10494          * @param {String} className
10495          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10496          * mouseout events for children elements
10497          * @return {Roo.Element} this
10498          */
10499         addClassOnOver : function(className, preventFlicker){
10500             this.on("mouseover", function(){
10501                 Roo.fly(this, '_internal').addClass(className);
10502             }, this.dom);
10503             var removeFn = function(e){
10504                 if(preventFlicker !== true || !e.within(this, true)){
10505                     Roo.fly(this, '_internal').removeClass(className);
10506                 }
10507             };
10508             this.on("mouseout", removeFn, this.dom);
10509             return this;
10510         },
10511
10512         /**
10513          * Sets up event handlers to add and remove a css class when this element has the focus
10514          * @param {String} className
10515          * @return {Roo.Element} this
10516          */
10517         addClassOnFocus : function(className){
10518             this.on("focus", function(){
10519                 Roo.fly(this, '_internal').addClass(className);
10520             }, this.dom);
10521             this.on("blur", function(){
10522                 Roo.fly(this, '_internal').removeClass(className);
10523             }, this.dom);
10524             return this;
10525         },
10526         /**
10527          * 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)
10528          * @param {String} className
10529          * @return {Roo.Element} this
10530          */
10531         addClassOnClick : function(className){
10532             var dom = this.dom;
10533             this.on("mousedown", function(){
10534                 Roo.fly(dom, '_internal').addClass(className);
10535                 var d = Roo.get(document);
10536                 var fn = function(){
10537                     Roo.fly(dom, '_internal').removeClass(className);
10538                     d.removeListener("mouseup", fn);
10539                 };
10540                 d.on("mouseup", fn);
10541             });
10542             return this;
10543         },
10544
10545         /**
10546          * Stops the specified event from bubbling and optionally prevents the default action
10547          * @param {String} eventName
10548          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10549          * @return {Roo.Element} this
10550          */
10551         swallowEvent : function(eventName, preventDefault){
10552             var fn = function(e){
10553                 e.stopPropagation();
10554                 if(preventDefault){
10555                     e.preventDefault();
10556                 }
10557             };
10558             if(eventName instanceof Array){
10559                 for(var i = 0, len = eventName.length; i < len; i++){
10560                      this.on(eventName[i], fn);
10561                 }
10562                 return this;
10563             }
10564             this.on(eventName, fn);
10565             return this;
10566         },
10567
10568         /**
10569          * @private
10570          */
10571         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10572
10573         /**
10574          * Sizes this element to its parent element's dimensions performing
10575          * neccessary box adjustments.
10576          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10577          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10578          * @return {Roo.Element} this
10579          */
10580         fitToParent : function(monitorResize, targetParent) {
10581           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10582           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10583           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10584             return this;
10585           }
10586           var p = Roo.get(targetParent || this.dom.parentNode);
10587           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10588           if (monitorResize === true) {
10589             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10590             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10591           }
10592           return this;
10593         },
10594
10595         /**
10596          * Gets the next sibling, skipping text nodes
10597          * @return {HTMLElement} The next sibling or null
10598          */
10599         getNextSibling : function(){
10600             var n = this.dom.nextSibling;
10601             while(n && n.nodeType != 1){
10602                 n = n.nextSibling;
10603             }
10604             return n;
10605         },
10606
10607         /**
10608          * Gets the previous sibling, skipping text nodes
10609          * @return {HTMLElement} The previous sibling or null
10610          */
10611         getPrevSibling : function(){
10612             var n = this.dom.previousSibling;
10613             while(n && n.nodeType != 1){
10614                 n = n.previousSibling;
10615             }
10616             return n;
10617         },
10618
10619
10620         /**
10621          * Appends the passed element(s) to this element
10622          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10623          * @return {Roo.Element} this
10624          */
10625         appendChild: function(el){
10626             el = Roo.get(el);
10627             el.appendTo(this);
10628             return this;
10629         },
10630
10631         /**
10632          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10633          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10634          * automatically generated with the specified attributes.
10635          * @param {HTMLElement} insertBefore (optional) a child element of this element
10636          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10637          * @return {Roo.Element} The new child element
10638          */
10639         createChild: function(config, insertBefore, returnDom){
10640             config = config || {tag:'div'};
10641             if(insertBefore){
10642                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10643             }
10644             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10645         },
10646
10647         /**
10648          * Appends this element to the passed element
10649          * @param {String/HTMLElement/Element} el The new parent element
10650          * @return {Roo.Element} this
10651          */
10652         appendTo: function(el){
10653             el = Roo.getDom(el);
10654             el.appendChild(this.dom);
10655             return this;
10656         },
10657
10658         /**
10659          * Inserts this element before the passed element in the DOM
10660          * @param {String/HTMLElement/Element} el The element to insert before
10661          * @return {Roo.Element} this
10662          */
10663         insertBefore: function(el){
10664             el = Roo.getDom(el);
10665             el.parentNode.insertBefore(this.dom, el);
10666             return this;
10667         },
10668
10669         /**
10670          * Inserts this element after the passed element in the DOM
10671          * @param {String/HTMLElement/Element} el The element to insert after
10672          * @return {Roo.Element} this
10673          */
10674         insertAfter: function(el){
10675             el = Roo.getDom(el);
10676             el.parentNode.insertBefore(this.dom, el.nextSibling);
10677             return this;
10678         },
10679
10680         /**
10681          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10682          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10683          * @return {Roo.Element} The new child
10684          */
10685         insertFirst: function(el, returnDom){
10686             el = el || {};
10687             if(typeof el == 'object' && !el.nodeType){ // dh config
10688                 return this.createChild(el, this.dom.firstChild, returnDom);
10689             }else{
10690                 el = Roo.getDom(el);
10691                 this.dom.insertBefore(el, this.dom.firstChild);
10692                 return !returnDom ? Roo.get(el) : el;
10693             }
10694         },
10695
10696         /**
10697          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10698          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10699          * @param {String} where (optional) 'before' or 'after' defaults to before
10700          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10701          * @return {Roo.Element} the inserted Element
10702          */
10703         insertSibling: function(el, where, returnDom){
10704             where = where ? where.toLowerCase() : 'before';
10705             el = el || {};
10706             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10707
10708             if(typeof el == 'object' && !el.nodeType){ // dh config
10709                 if(where == 'after' && !this.dom.nextSibling){
10710                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10711                 }else{
10712                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10713                 }
10714
10715             }else{
10716                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10717                             where == 'before' ? this.dom : this.dom.nextSibling);
10718                 if(!returnDom){
10719                     rt = Roo.get(rt);
10720                 }
10721             }
10722             return rt;
10723         },
10724
10725         /**
10726          * Creates and wraps this element with another element
10727          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10728          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10729          * @return {HTMLElement/Element} The newly created wrapper element
10730          */
10731         wrap: function(config, returnDom){
10732             if(!config){
10733                 config = {tag: "div"};
10734             }
10735             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10736             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10737             return newEl;
10738         },
10739
10740         /**
10741          * Replaces the passed element with this element
10742          * @param {String/HTMLElement/Element} el The element to replace
10743          * @return {Roo.Element} this
10744          */
10745         replace: function(el){
10746             el = Roo.get(el);
10747             this.insertBefore(el);
10748             el.remove();
10749             return this;
10750         },
10751
10752         /**
10753          * Inserts an html fragment into this element
10754          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10755          * @param {String} html The HTML fragment
10756          * @param {Boolean} returnEl True to return an Roo.Element
10757          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10758          */
10759         insertHtml : function(where, html, returnEl){
10760             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10761             return returnEl ? Roo.get(el) : el;
10762         },
10763
10764         /**
10765          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10766          * @param {Object} o The object with the attributes
10767          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10768          * @return {Roo.Element} this
10769          */
10770         set : function(o, useSet){
10771             var el = this.dom;
10772             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10773             for(var attr in o){
10774                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10775                 if(attr=="cls"){
10776                     el.className = o["cls"];
10777                 }else{
10778                     if(useSet) {
10779                         el.setAttribute(attr, o[attr]);
10780                     } else {
10781                         el[attr] = o[attr];
10782                     }
10783                 }
10784             }
10785             if(o.style){
10786                 Roo.DomHelper.applyStyles(el, o.style);
10787             }
10788             return this;
10789         },
10790
10791         /**
10792          * Convenience method for constructing a KeyMap
10793          * @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:
10794          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10795          * @param {Function} fn The function to call
10796          * @param {Object} scope (optional) The scope of the function
10797          * @return {Roo.KeyMap} The KeyMap created
10798          */
10799         addKeyListener : function(key, fn, scope){
10800             var config;
10801             if(typeof key != "object" || key instanceof Array){
10802                 config = {
10803                     key: key,
10804                     fn: fn,
10805                     scope: scope
10806                 };
10807             }else{
10808                 config = {
10809                     key : key.key,
10810                     shift : key.shift,
10811                     ctrl : key.ctrl,
10812                     alt : key.alt,
10813                     fn: fn,
10814                     scope: scope
10815                 };
10816             }
10817             return new Roo.KeyMap(this, config);
10818         },
10819
10820         /**
10821          * Creates a KeyMap for this element
10822          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10823          * @return {Roo.KeyMap} The KeyMap created
10824          */
10825         addKeyMap : function(config){
10826             return new Roo.KeyMap(this, config);
10827         },
10828
10829         /**
10830          * Returns true if this element is scrollable.
10831          * @return {Boolean}
10832          */
10833          isScrollable : function(){
10834             var dom = this.dom;
10835             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10836         },
10837
10838         /**
10839          * 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().
10840          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10841          * @param {Number} value The new scroll value
10842          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10843          * @return {Element} this
10844          */
10845
10846         scrollTo : function(side, value, animate){
10847             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10848             if(!animate || !A){
10849                 this.dom[prop] = value;
10850             }else{
10851                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10852                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10853             }
10854             return this;
10855         },
10856
10857         /**
10858          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10859          * within this element's scrollable range.
10860          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10861          * @param {Number} distance How far to scroll the element in pixels
10862          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10863          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10864          * was scrolled as far as it could go.
10865          */
10866          scroll : function(direction, distance, animate){
10867              if(!this.isScrollable()){
10868                  return;
10869              }
10870              var el = this.dom;
10871              var l = el.scrollLeft, t = el.scrollTop;
10872              var w = el.scrollWidth, h = el.scrollHeight;
10873              var cw = el.clientWidth, ch = el.clientHeight;
10874              direction = direction.toLowerCase();
10875              var scrolled = false;
10876              var a = this.preanim(arguments, 2);
10877              switch(direction){
10878                  case "l":
10879                  case "left":
10880                      if(w - l > cw){
10881                          var v = Math.min(l + distance, w-cw);
10882                          this.scrollTo("left", v, a);
10883                          scrolled = true;
10884                      }
10885                      break;
10886                 case "r":
10887                 case "right":
10888                      if(l > 0){
10889                          var v = Math.max(l - distance, 0);
10890                          this.scrollTo("left", v, a);
10891                          scrolled = true;
10892                      }
10893                      break;
10894                 case "t":
10895                 case "top":
10896                 case "up":
10897                      if(t > 0){
10898                          var v = Math.max(t - distance, 0);
10899                          this.scrollTo("top", v, a);
10900                          scrolled = true;
10901                      }
10902                      break;
10903                 case "b":
10904                 case "bottom":
10905                 case "down":
10906                      if(h - t > ch){
10907                          var v = Math.min(t + distance, h-ch);
10908                          this.scrollTo("top", v, a);
10909                          scrolled = true;
10910                      }
10911                      break;
10912              }
10913              return scrolled;
10914         },
10915
10916         /**
10917          * Translates the passed page coordinates into left/top css values for this element
10918          * @param {Number/Array} x The page x or an array containing [x, y]
10919          * @param {Number} y The page y
10920          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10921          */
10922         translatePoints : function(x, y){
10923             if(typeof x == 'object' || x instanceof Array){
10924                 y = x[1]; x = x[0];
10925             }
10926             var p = this.getStyle('position');
10927             var o = this.getXY();
10928
10929             var l = parseInt(this.getStyle('left'), 10);
10930             var t = parseInt(this.getStyle('top'), 10);
10931
10932             if(isNaN(l)){
10933                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10934             }
10935             if(isNaN(t)){
10936                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10937             }
10938
10939             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10940         },
10941
10942         /**
10943          * Returns the current scroll position of the element.
10944          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10945          */
10946         getScroll : function(){
10947             var d = this.dom, doc = document;
10948             if(d == doc || d == doc.body){
10949                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10950                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10951                 return {left: l, top: t};
10952             }else{
10953                 return {left: d.scrollLeft, top: d.scrollTop};
10954             }
10955         },
10956
10957         /**
10958          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10959          * are convert to standard 6 digit hex color.
10960          * @param {String} attr The css attribute
10961          * @param {String} defaultValue The default value to use when a valid color isn't found
10962          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10963          * YUI color anims.
10964          */
10965         getColor : function(attr, defaultValue, prefix){
10966             var v = this.getStyle(attr);
10967             if(!v || v == "transparent" || v == "inherit") {
10968                 return defaultValue;
10969             }
10970             var color = typeof prefix == "undefined" ? "#" : prefix;
10971             if(v.substr(0, 4) == "rgb("){
10972                 var rvs = v.slice(4, v.length -1).split(",");
10973                 for(var i = 0; i < 3; i++){
10974                     var h = parseInt(rvs[i]).toString(16);
10975                     if(h < 16){
10976                         h = "0" + h;
10977                     }
10978                     color += h;
10979                 }
10980             } else {
10981                 if(v.substr(0, 1) == "#"){
10982                     if(v.length == 4) {
10983                         for(var i = 1; i < 4; i++){
10984                             var c = v.charAt(i);
10985                             color +=  c + c;
10986                         }
10987                     }else if(v.length == 7){
10988                         color += v.substr(1);
10989                     }
10990                 }
10991             }
10992             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10993         },
10994
10995         /**
10996          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10997          * gradient background, rounded corners and a 4-way shadow.
10998          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10999          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
11000          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
11001          * @return {Roo.Element} this
11002          */
11003         boxWrap : function(cls){
11004             cls = cls || 'x-box';
11005             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
11006             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
11007             return el;
11008         },
11009
11010         /**
11011          * Returns the value of a namespaced attribute from the element's underlying DOM node.
11012          * @param {String} namespace The namespace in which to look for the attribute
11013          * @param {String} name The attribute name
11014          * @return {String} The attribute value
11015          */
11016         getAttributeNS : Roo.isIE ? function(ns, name){
11017             var d = this.dom;
11018             var type = typeof d[ns+":"+name];
11019             if(type != 'undefined' && type != 'unknown'){
11020                 return d[ns+":"+name];
11021             }
11022             return d[name];
11023         } : function(ns, name){
11024             var d = this.dom;
11025             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
11026         },
11027         
11028         
11029         /**
11030          * Sets or Returns the value the dom attribute value
11031          * @param {String|Object} name The attribute name (or object to set multiple attributes)
11032          * @param {String} value (optional) The value to set the attribute to
11033          * @return {String} The attribute value
11034          */
11035         attr : function(name){
11036             if (arguments.length > 1) {
11037                 this.dom.setAttribute(name, arguments[1]);
11038                 return arguments[1];
11039             }
11040             if (typeof(name) == 'object') {
11041                 for(var i in name) {
11042                     this.attr(i, name[i]);
11043                 }
11044                 return name;
11045             }
11046             
11047             
11048             if (!this.dom.hasAttribute(name)) {
11049                 return undefined;
11050             }
11051             return this.dom.getAttribute(name);
11052         }
11053         
11054         
11055         
11056     };
11057
11058     var ep = El.prototype;
11059
11060     /**
11061      * Appends an event handler (Shorthand for addListener)
11062      * @param {String}   eventName     The type of event to append
11063      * @param {Function} fn        The method the event invokes
11064      * @param {Object} scope       (optional) The scope (this object) of the fn
11065      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
11066      * @method
11067      */
11068     ep.on = ep.addListener;
11069         // backwards compat
11070     ep.mon = ep.addListener;
11071
11072     /**
11073      * Removes an event handler from this element (shorthand for removeListener)
11074      * @param {String} eventName the type of event to remove
11075      * @param {Function} fn the method the event invokes
11076      * @return {Roo.Element} this
11077      * @method
11078      */
11079     ep.un = ep.removeListener;
11080
11081     /**
11082      * true to automatically adjust width and height settings for box-model issues (default to true)
11083      */
11084     ep.autoBoxAdjust = true;
11085
11086     // private
11087     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
11088
11089     // private
11090     El.addUnits = function(v, defaultUnit){
11091         if(v === "" || v == "auto"){
11092             return v;
11093         }
11094         if(v === undefined){
11095             return '';
11096         }
11097         if(typeof v == "number" || !El.unitPattern.test(v)){
11098             return v + (defaultUnit || 'px');
11099         }
11100         return v;
11101     };
11102
11103     // special markup used throughout Roo when box wrapping elements
11104     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>';
11105     /**
11106      * Visibility mode constant - Use visibility to hide element
11107      * @static
11108      * @type Number
11109      */
11110     El.VISIBILITY = 1;
11111     /**
11112      * Visibility mode constant - Use display to hide element
11113      * @static
11114      * @type Number
11115      */
11116     El.DISPLAY = 2;
11117
11118     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
11119     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
11120     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
11121
11122
11123
11124     /**
11125      * @private
11126      */
11127     El.cache = {};
11128
11129     var docEl;
11130
11131     /**
11132      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11133      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11134      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11135      * @return {Element} The Element object
11136      * @static
11137      */
11138     El.get = function(el){
11139         var ex, elm, id;
11140         if(!el){ return null; }
11141         if(typeof el == "string"){ // element id
11142             if(!(elm = document.getElementById(el))){
11143                 return null;
11144             }
11145             if(ex = El.cache[el]){
11146                 ex.dom = elm;
11147             }else{
11148                 ex = El.cache[el] = new El(elm);
11149             }
11150             return ex;
11151         }else if(el.tagName){ // dom element
11152             if(!(id = el.id)){
11153                 id = Roo.id(el);
11154             }
11155             if(ex = El.cache[id]){
11156                 ex.dom = el;
11157             }else{
11158                 ex = El.cache[id] = new El(el);
11159             }
11160             return ex;
11161         }else if(el instanceof El){
11162             if(el != docEl){
11163                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
11164                                                               // catch case where it hasn't been appended
11165                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
11166             }
11167             return el;
11168         }else if(el.isComposite){
11169             return el;
11170         }else if(el instanceof Array){
11171             return El.select(el);
11172         }else if(el == document){
11173             // create a bogus element object representing the document object
11174             if(!docEl){
11175                 var f = function(){};
11176                 f.prototype = El.prototype;
11177                 docEl = new f();
11178                 docEl.dom = document;
11179             }
11180             return docEl;
11181         }
11182         return null;
11183     };
11184
11185     // private
11186     El.uncache = function(el){
11187         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
11188             if(a[i]){
11189                 delete El.cache[a[i].id || a[i]];
11190             }
11191         }
11192     };
11193
11194     // private
11195     // Garbage collection - uncache elements/purge listeners on orphaned elements
11196     // so we don't hold a reference and cause the browser to retain them
11197     El.garbageCollect = function(){
11198         if(!Roo.enableGarbageCollector){
11199             clearInterval(El.collectorThread);
11200             return;
11201         }
11202         for(var eid in El.cache){
11203             var el = El.cache[eid], d = el.dom;
11204             // -------------------------------------------------------
11205             // Determining what is garbage:
11206             // -------------------------------------------------------
11207             // !d
11208             // dom node is null, definitely garbage
11209             // -------------------------------------------------------
11210             // !d.parentNode
11211             // no parentNode == direct orphan, definitely garbage
11212             // -------------------------------------------------------
11213             // !d.offsetParent && !document.getElementById(eid)
11214             // display none elements have no offsetParent so we will
11215             // also try to look it up by it's id. However, check
11216             // offsetParent first so we don't do unneeded lookups.
11217             // This enables collection of elements that are not orphans
11218             // directly, but somewhere up the line they have an orphan
11219             // parent.
11220             // -------------------------------------------------------
11221             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
11222                 delete El.cache[eid];
11223                 if(d && Roo.enableListenerCollection){
11224                     E.purgeElement(d);
11225                 }
11226             }
11227         }
11228     }
11229     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
11230
11231
11232     // dom is optional
11233     El.Flyweight = function(dom){
11234         this.dom = dom;
11235     };
11236     El.Flyweight.prototype = El.prototype;
11237
11238     El._flyweights = {};
11239     /**
11240      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11241      * the dom node can be overwritten by other code.
11242      * @param {String/HTMLElement} el The dom node or id
11243      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11244      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11245      * @static
11246      * @return {Element} The shared Element object
11247      */
11248     El.fly = function(el, named){
11249         named = named || '_global';
11250         el = Roo.getDom(el);
11251         if(!el){
11252             return null;
11253         }
11254         if(!El._flyweights[named]){
11255             El._flyweights[named] = new El.Flyweight();
11256         }
11257         El._flyweights[named].dom = el;
11258         return El._flyweights[named];
11259     };
11260
11261     /**
11262      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
11263      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
11264      * Shorthand of {@link Roo.Element#get}
11265      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
11266      * @return {Element} The Element object
11267      * @member Roo
11268      * @method get
11269      */
11270     Roo.get = El.get;
11271     /**
11272      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
11273      * the dom node can be overwritten by other code.
11274      * Shorthand of {@link Roo.Element#fly}
11275      * @param {String/HTMLElement} el The dom node or id
11276      * @param {String} named (optional) Allows for creation of named reusable flyweights to
11277      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
11278      * @static
11279      * @return {Element} The shared Element object
11280      * @member Roo
11281      * @method fly
11282      */
11283     Roo.fly = El.fly;
11284
11285     // speedy lookup for elements never to box adjust
11286     var noBoxAdjust = Roo.isStrict ? {
11287         select:1
11288     } : {
11289         input:1, select:1, textarea:1
11290     };
11291     if(Roo.isIE || Roo.isGecko){
11292         noBoxAdjust['button'] = 1;
11293     }
11294
11295
11296     Roo.EventManager.on(window, 'unload', function(){
11297         delete El.cache;
11298         delete El._flyweights;
11299     });
11300 })();
11301
11302
11303
11304
11305 if(Roo.DomQuery){
11306     Roo.Element.selectorFunction = Roo.DomQuery.select;
11307 }
11308
11309 Roo.Element.select = function(selector, unique, root){
11310     var els;
11311     if(typeof selector == "string"){
11312         els = Roo.Element.selectorFunction(selector, root);
11313     }else if(selector.length !== undefined){
11314         els = selector;
11315     }else{
11316         throw "Invalid selector";
11317     }
11318     if(unique === true){
11319         return new Roo.CompositeElement(els);
11320     }else{
11321         return new Roo.CompositeElementLite(els);
11322     }
11323 };
11324 /**
11325  * Selects elements based on the passed CSS selector to enable working on them as 1.
11326  * @param {String/Array} selector The CSS selector or an array of elements
11327  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11328  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11329  * @return {CompositeElementLite/CompositeElement}
11330  * @member Roo
11331  * @method select
11332  */
11333 Roo.select = Roo.Element.select;
11334
11335
11336
11337
11338
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348 /*
11349  * Based on:
11350  * Ext JS Library 1.1.1
11351  * Copyright(c) 2006-2007, Ext JS, LLC.
11352  *
11353  * Originally Released Under LGPL - original licence link has changed is not relivant.
11354  *
11355  * Fork - LGPL
11356  * <script type="text/javascript">
11357  */
11358
11359
11360
11361 //Notifies Element that fx methods are available
11362 Roo.enableFx = true;
11363
11364 /**
11365  * @class Roo.Fx
11366  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11367  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11368  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11369  * Element effects to work.</p><br/>
11370  *
11371  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11372  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11373  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11374  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11375  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11376  * expected results and should be done with care.</p><br/>
11377  *
11378  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11379  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11380 <pre>
11381 Value  Description
11382 -----  -----------------------------
11383 tl     The top left corner
11384 t      The center of the top edge
11385 tr     The top right corner
11386 l      The center of the left edge
11387 r      The center of the right edge
11388 bl     The bottom left corner
11389 b      The center of the bottom edge
11390 br     The bottom right corner
11391 </pre>
11392  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11393  * below are common options that can be passed to any Fx method.</b>
11394  * @cfg {Function} callback A function called when the effect is finished
11395  * @cfg {Object} scope The scope of the effect function
11396  * @cfg {String} easing A valid Easing value for the effect
11397  * @cfg {String} afterCls A css class to apply after the effect
11398  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11399  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11400  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11401  * effects that end with the element being visually hidden, ignored otherwise)
11402  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11403  * a function which returns such a specification that will be applied to the Element after the effect finishes
11404  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11405  * @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
11406  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11407  */
11408 Roo.Fx = {
11409         /**
11410          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11411          * origin for the slide effect.  This function automatically handles wrapping the element with
11412          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11413          * Usage:
11414          *<pre><code>
11415 // default: slide the element in from the top
11416 el.slideIn();
11417
11418 // custom: slide the element in from the right with a 2-second duration
11419 el.slideIn('r', { duration: 2 });
11420
11421 // common config options shown with default values
11422 el.slideIn('t', {
11423     easing: 'easeOut',
11424     duration: .5
11425 });
11426 </code></pre>
11427          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11428          * @param {Object} options (optional) Object literal with any of the Fx config options
11429          * @return {Roo.Element} The Element
11430          */
11431     slideIn : function(anchor, o){
11432         var el = this.getFxEl();
11433         o = o || {};
11434
11435         el.queueFx(o, function(){
11436
11437             anchor = anchor || "t";
11438
11439             // fix display to visibility
11440             this.fixDisplay();
11441
11442             // restore values after effect
11443             var r = this.getFxRestore();
11444             var b = this.getBox();
11445             // fixed size for slide
11446             this.setSize(b);
11447
11448             // wrap if needed
11449             var wrap = this.fxWrap(r.pos, o, "hidden");
11450
11451             var st = this.dom.style;
11452             st.visibility = "visible";
11453             st.position = "absolute";
11454
11455             // clear out temp styles after slide and unwrap
11456             var after = function(){
11457                 el.fxUnwrap(wrap, r.pos, o);
11458                 st.width = r.width;
11459                 st.height = r.height;
11460                 el.afterFx(o);
11461             };
11462             // time to calc the positions
11463             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11464
11465             switch(anchor.toLowerCase()){
11466                 case "t":
11467                     wrap.setSize(b.width, 0);
11468                     st.left = st.bottom = "0";
11469                     a = {height: bh};
11470                 break;
11471                 case "l":
11472                     wrap.setSize(0, b.height);
11473                     st.right = st.top = "0";
11474                     a = {width: bw};
11475                 break;
11476                 case "r":
11477                     wrap.setSize(0, b.height);
11478                     wrap.setX(b.right);
11479                     st.left = st.top = "0";
11480                     a = {width: bw, points: pt};
11481                 break;
11482                 case "b":
11483                     wrap.setSize(b.width, 0);
11484                     wrap.setY(b.bottom);
11485                     st.left = st.top = "0";
11486                     a = {height: bh, points: pt};
11487                 break;
11488                 case "tl":
11489                     wrap.setSize(0, 0);
11490                     st.right = st.bottom = "0";
11491                     a = {width: bw, height: bh};
11492                 break;
11493                 case "bl":
11494                     wrap.setSize(0, 0);
11495                     wrap.setY(b.y+b.height);
11496                     st.right = st.top = "0";
11497                     a = {width: bw, height: bh, points: pt};
11498                 break;
11499                 case "br":
11500                     wrap.setSize(0, 0);
11501                     wrap.setXY([b.right, b.bottom]);
11502                     st.left = st.top = "0";
11503                     a = {width: bw, height: bh, points: pt};
11504                 break;
11505                 case "tr":
11506                     wrap.setSize(0, 0);
11507                     wrap.setX(b.x+b.width);
11508                     st.left = st.bottom = "0";
11509                     a = {width: bw, height: bh, points: pt};
11510                 break;
11511             }
11512             this.dom.style.visibility = "visible";
11513             wrap.show();
11514
11515             arguments.callee.anim = wrap.fxanim(a,
11516                 o,
11517                 'motion',
11518                 .5,
11519                 'easeOut', after);
11520         });
11521         return this;
11522     },
11523     
11524         /**
11525          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11526          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11527          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11528          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11529          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11530          * Usage:
11531          *<pre><code>
11532 // default: slide the element out to the top
11533 el.slideOut();
11534
11535 // custom: slide the element out to the right with a 2-second duration
11536 el.slideOut('r', { duration: 2 });
11537
11538 // common config options shown with default values
11539 el.slideOut('t', {
11540     easing: 'easeOut',
11541     duration: .5,
11542     remove: false,
11543     useDisplay: false
11544 });
11545 </code></pre>
11546          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11547          * @param {Object} options (optional) Object literal with any of the Fx config options
11548          * @return {Roo.Element} The Element
11549          */
11550     slideOut : function(anchor, o){
11551         var el = this.getFxEl();
11552         o = o || {};
11553
11554         el.queueFx(o, function(){
11555
11556             anchor = anchor || "t";
11557
11558             // restore values after effect
11559             var r = this.getFxRestore();
11560             
11561             var b = this.getBox();
11562             // fixed size for slide
11563             this.setSize(b);
11564
11565             // wrap if needed
11566             var wrap = this.fxWrap(r.pos, o, "visible");
11567
11568             var st = this.dom.style;
11569             st.visibility = "visible";
11570             st.position = "absolute";
11571
11572             wrap.setSize(b);
11573
11574             var after = function(){
11575                 if(o.useDisplay){
11576                     el.setDisplayed(false);
11577                 }else{
11578                     el.hide();
11579                 }
11580
11581                 el.fxUnwrap(wrap, r.pos, o);
11582
11583                 st.width = r.width;
11584                 st.height = r.height;
11585
11586                 el.afterFx(o);
11587             };
11588
11589             var a, zero = {to: 0};
11590             switch(anchor.toLowerCase()){
11591                 case "t":
11592                     st.left = st.bottom = "0";
11593                     a = {height: zero};
11594                 break;
11595                 case "l":
11596                     st.right = st.top = "0";
11597                     a = {width: zero};
11598                 break;
11599                 case "r":
11600                     st.left = st.top = "0";
11601                     a = {width: zero, points: {to:[b.right, b.y]}};
11602                 break;
11603                 case "b":
11604                     st.left = st.top = "0";
11605                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11606                 break;
11607                 case "tl":
11608                     st.right = st.bottom = "0";
11609                     a = {width: zero, height: zero};
11610                 break;
11611                 case "bl":
11612                     st.right = st.top = "0";
11613                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11614                 break;
11615                 case "br":
11616                     st.left = st.top = "0";
11617                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11618                 break;
11619                 case "tr":
11620                     st.left = st.bottom = "0";
11621                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11622                 break;
11623             }
11624
11625             arguments.callee.anim = wrap.fxanim(a,
11626                 o,
11627                 'motion',
11628                 .5,
11629                 "easeOut", after);
11630         });
11631         return this;
11632     },
11633
11634         /**
11635          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11636          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11637          * The element must be removed from the DOM using the 'remove' config option if desired.
11638          * Usage:
11639          *<pre><code>
11640 // default
11641 el.puff();
11642
11643 // common config options shown with default values
11644 el.puff({
11645     easing: 'easeOut',
11646     duration: .5,
11647     remove: false,
11648     useDisplay: false
11649 });
11650 </code></pre>
11651          * @param {Object} options (optional) Object literal with any of the Fx config options
11652          * @return {Roo.Element} The Element
11653          */
11654     puff : function(o){
11655         var el = this.getFxEl();
11656         o = o || {};
11657
11658         el.queueFx(o, function(){
11659             this.clearOpacity();
11660             this.show();
11661
11662             // restore values after effect
11663             var r = this.getFxRestore();
11664             var st = this.dom.style;
11665
11666             var after = function(){
11667                 if(o.useDisplay){
11668                     el.setDisplayed(false);
11669                 }else{
11670                     el.hide();
11671                 }
11672
11673                 el.clearOpacity();
11674
11675                 el.setPositioning(r.pos);
11676                 st.width = r.width;
11677                 st.height = r.height;
11678                 st.fontSize = '';
11679                 el.afterFx(o);
11680             };
11681
11682             var width = this.getWidth();
11683             var height = this.getHeight();
11684
11685             arguments.callee.anim = this.fxanim({
11686                     width : {to: this.adjustWidth(width * 2)},
11687                     height : {to: this.adjustHeight(height * 2)},
11688                     points : {by: [-(width * .5), -(height * .5)]},
11689                     opacity : {to: 0},
11690                     fontSize: {to:200, unit: "%"}
11691                 },
11692                 o,
11693                 'motion',
11694                 .5,
11695                 "easeOut", after);
11696         });
11697         return this;
11698     },
11699
11700         /**
11701          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11702          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11703          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11704          * Usage:
11705          *<pre><code>
11706 // default
11707 el.switchOff();
11708
11709 // all config options shown with default values
11710 el.switchOff({
11711     easing: 'easeIn',
11712     duration: .3,
11713     remove: false,
11714     useDisplay: false
11715 });
11716 </code></pre>
11717          * @param {Object} options (optional) Object literal with any of the Fx config options
11718          * @return {Roo.Element} The Element
11719          */
11720     switchOff : function(o){
11721         var el = this.getFxEl();
11722         o = o || {};
11723
11724         el.queueFx(o, function(){
11725             this.clearOpacity();
11726             this.clip();
11727
11728             // restore values after effect
11729             var r = this.getFxRestore();
11730             var st = this.dom.style;
11731
11732             var after = function(){
11733                 if(o.useDisplay){
11734                     el.setDisplayed(false);
11735                 }else{
11736                     el.hide();
11737                 }
11738
11739                 el.clearOpacity();
11740                 el.setPositioning(r.pos);
11741                 st.width = r.width;
11742                 st.height = r.height;
11743
11744                 el.afterFx(o);
11745             };
11746
11747             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11748                 this.clearOpacity();
11749                 (function(){
11750                     this.fxanim({
11751                         height:{to:1},
11752                         points:{by:[0, this.getHeight() * .5]}
11753                     }, o, 'motion', 0.3, 'easeIn', after);
11754                 }).defer(100, this);
11755             });
11756         });
11757         return this;
11758     },
11759
11760     /**
11761      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11762      * changed using the "attr" config option) and then fading back to the original color. If no original
11763      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11764      * Usage:
11765 <pre><code>
11766 // default: highlight background to yellow
11767 el.highlight();
11768
11769 // custom: highlight foreground text to blue for 2 seconds
11770 el.highlight("0000ff", { attr: 'color', duration: 2 });
11771
11772 // common config options shown with default values
11773 el.highlight("ffff9c", {
11774     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11775     endColor: (current color) or "ffffff",
11776     easing: 'easeIn',
11777     duration: 1
11778 });
11779 </code></pre>
11780      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11781      * @param {Object} options (optional) Object literal with any of the Fx config options
11782      * @return {Roo.Element} The Element
11783      */ 
11784     highlight : function(color, o){
11785         var el = this.getFxEl();
11786         o = o || {};
11787
11788         el.queueFx(o, function(){
11789             color = color || "ffff9c";
11790             attr = o.attr || "backgroundColor";
11791
11792             this.clearOpacity();
11793             this.show();
11794
11795             var origColor = this.getColor(attr);
11796             var restoreColor = this.dom.style[attr];
11797             endColor = (o.endColor || origColor) || "ffffff";
11798
11799             var after = function(){
11800                 el.dom.style[attr] = restoreColor;
11801                 el.afterFx(o);
11802             };
11803
11804             var a = {};
11805             a[attr] = {from: color, to: endColor};
11806             arguments.callee.anim = this.fxanim(a,
11807                 o,
11808                 'color',
11809                 1,
11810                 'easeIn', after);
11811         });
11812         return this;
11813     },
11814
11815    /**
11816     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11817     * Usage:
11818 <pre><code>
11819 // default: a single light blue ripple
11820 el.frame();
11821
11822 // custom: 3 red ripples lasting 3 seconds total
11823 el.frame("ff0000", 3, { duration: 3 });
11824
11825 // common config options shown with default values
11826 el.frame("C3DAF9", 1, {
11827     duration: 1 //duration of entire animation (not each individual ripple)
11828     // Note: Easing is not configurable and will be ignored if included
11829 });
11830 </code></pre>
11831     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11832     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11833     * @param {Object} options (optional) Object literal with any of the Fx config options
11834     * @return {Roo.Element} The Element
11835     */
11836     frame : function(color, count, o){
11837         var el = this.getFxEl();
11838         o = o || {};
11839
11840         el.queueFx(o, function(){
11841             color = color || "#C3DAF9";
11842             if(color.length == 6){
11843                 color = "#" + color;
11844             }
11845             count = count || 1;
11846             duration = o.duration || 1;
11847             this.show();
11848
11849             var b = this.getBox();
11850             var animFn = function(){
11851                 var proxy = this.createProxy({
11852
11853                      style:{
11854                         visbility:"hidden",
11855                         position:"absolute",
11856                         "z-index":"35000", // yee haw
11857                         border:"0px solid " + color
11858                      }
11859                   });
11860                 var scale = Roo.isBorderBox ? 2 : 1;
11861                 proxy.animate({
11862                     top:{from:b.y, to:b.y - 20},
11863                     left:{from:b.x, to:b.x - 20},
11864                     borderWidth:{from:0, to:10},
11865                     opacity:{from:1, to:0},
11866                     height:{from:b.height, to:(b.height + (20*scale))},
11867                     width:{from:b.width, to:(b.width + (20*scale))}
11868                 }, duration, function(){
11869                     proxy.remove();
11870                 });
11871                 if(--count > 0){
11872                      animFn.defer((duration/2)*1000, this);
11873                 }else{
11874                     el.afterFx(o);
11875                 }
11876             };
11877             animFn.call(this);
11878         });
11879         return this;
11880     },
11881
11882    /**
11883     * Creates a pause before any subsequent queued effects begin.  If there are
11884     * no effects queued after the pause it will have no effect.
11885     * Usage:
11886 <pre><code>
11887 el.pause(1);
11888 </code></pre>
11889     * @param {Number} seconds The length of time to pause (in seconds)
11890     * @return {Roo.Element} The Element
11891     */
11892     pause : function(seconds){
11893         var el = this.getFxEl();
11894         var o = {};
11895
11896         el.queueFx(o, function(){
11897             setTimeout(function(){
11898                 el.afterFx(o);
11899             }, seconds * 1000);
11900         });
11901         return this;
11902     },
11903
11904    /**
11905     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11906     * using the "endOpacity" config option.
11907     * Usage:
11908 <pre><code>
11909 // default: fade in from opacity 0 to 100%
11910 el.fadeIn();
11911
11912 // custom: fade in from opacity 0 to 75% over 2 seconds
11913 el.fadeIn({ endOpacity: .75, duration: 2});
11914
11915 // common config options shown with default values
11916 el.fadeIn({
11917     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11918     easing: 'easeOut',
11919     duration: .5
11920 });
11921 </code></pre>
11922     * @param {Object} options (optional) Object literal with any of the Fx config options
11923     * @return {Roo.Element} The Element
11924     */
11925     fadeIn : function(o){
11926         var el = this.getFxEl();
11927         o = o || {};
11928         el.queueFx(o, function(){
11929             this.setOpacity(0);
11930             this.fixDisplay();
11931             this.dom.style.visibility = 'visible';
11932             var to = o.endOpacity || 1;
11933             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11934                 o, null, .5, "easeOut", function(){
11935                 if(to == 1){
11936                     this.clearOpacity();
11937                 }
11938                 el.afterFx(o);
11939             });
11940         });
11941         return this;
11942     },
11943
11944    /**
11945     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11946     * using the "endOpacity" config option.
11947     * Usage:
11948 <pre><code>
11949 // default: fade out from the element's current opacity to 0
11950 el.fadeOut();
11951
11952 // custom: fade out from the element's current opacity to 25% over 2 seconds
11953 el.fadeOut({ endOpacity: .25, duration: 2});
11954
11955 // common config options shown with default values
11956 el.fadeOut({
11957     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11958     easing: 'easeOut',
11959     duration: .5
11960     remove: false,
11961     useDisplay: false
11962 });
11963 </code></pre>
11964     * @param {Object} options (optional) Object literal with any of the Fx config options
11965     * @return {Roo.Element} The Element
11966     */
11967     fadeOut : function(o){
11968         var el = this.getFxEl();
11969         o = o || {};
11970         el.queueFx(o, function(){
11971             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11972                 o, null, .5, "easeOut", function(){
11973                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11974                      this.dom.style.display = "none";
11975                 }else{
11976                      this.dom.style.visibility = "hidden";
11977                 }
11978                 this.clearOpacity();
11979                 el.afterFx(o);
11980             });
11981         });
11982         return this;
11983     },
11984
11985    /**
11986     * Animates the transition of an element's dimensions from a starting height/width
11987     * to an ending height/width.
11988     * Usage:
11989 <pre><code>
11990 // change height and width to 100x100 pixels
11991 el.scale(100, 100);
11992
11993 // common config options shown with default values.  The height and width will default to
11994 // the element's existing values if passed as null.
11995 el.scale(
11996     [element's width],
11997     [element's height], {
11998     easing: 'easeOut',
11999     duration: .35
12000 });
12001 </code></pre>
12002     * @param {Number} width  The new width (pass undefined to keep the original width)
12003     * @param {Number} height  The new height (pass undefined to keep the original height)
12004     * @param {Object} options (optional) Object literal with any of the Fx config options
12005     * @return {Roo.Element} The Element
12006     */
12007     scale : function(w, h, o){
12008         this.shift(Roo.apply({}, o, {
12009             width: w,
12010             height: h
12011         }));
12012         return this;
12013     },
12014
12015    /**
12016     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
12017     * Any of these properties not specified in the config object will not be changed.  This effect 
12018     * requires that at least one new dimension, position or opacity setting must be passed in on
12019     * the config object in order for the function to have any effect.
12020     * Usage:
12021 <pre><code>
12022 // slide the element horizontally to x position 200 while changing the height and opacity
12023 el.shift({ x: 200, height: 50, opacity: .8 });
12024
12025 // common config options shown with default values.
12026 el.shift({
12027     width: [element's width],
12028     height: [element's height],
12029     x: [element's x position],
12030     y: [element's y position],
12031     opacity: [element's opacity],
12032     easing: 'easeOut',
12033     duration: .35
12034 });
12035 </code></pre>
12036     * @param {Object} options  Object literal with any of the Fx config options
12037     * @return {Roo.Element} The Element
12038     */
12039     shift : function(o){
12040         var el = this.getFxEl();
12041         o = o || {};
12042         el.queueFx(o, function(){
12043             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
12044             if(w !== undefined){
12045                 a.width = {to: this.adjustWidth(w)};
12046             }
12047             if(h !== undefined){
12048                 a.height = {to: this.adjustHeight(h)};
12049             }
12050             if(x !== undefined || y !== undefined){
12051                 a.points = {to: [
12052                     x !== undefined ? x : this.getX(),
12053                     y !== undefined ? y : this.getY()
12054                 ]};
12055             }
12056             if(op !== undefined){
12057                 a.opacity = {to: op};
12058             }
12059             if(o.xy !== undefined){
12060                 a.points = {to: o.xy};
12061             }
12062             arguments.callee.anim = this.fxanim(a,
12063                 o, 'motion', .35, "easeOut", function(){
12064                 el.afterFx(o);
12065             });
12066         });
12067         return this;
12068     },
12069
12070         /**
12071          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
12072          * ending point of the effect.
12073          * Usage:
12074          *<pre><code>
12075 // default: slide the element downward while fading out
12076 el.ghost();
12077
12078 // custom: slide the element out to the right with a 2-second duration
12079 el.ghost('r', { duration: 2 });
12080
12081 // common config options shown with default values
12082 el.ghost('b', {
12083     easing: 'easeOut',
12084     duration: .5
12085     remove: false,
12086     useDisplay: false
12087 });
12088 </code></pre>
12089          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
12090          * @param {Object} options (optional) Object literal with any of the Fx config options
12091          * @return {Roo.Element} The Element
12092          */
12093     ghost : function(anchor, o){
12094         var el = this.getFxEl();
12095         o = o || {};
12096
12097         el.queueFx(o, function(){
12098             anchor = anchor || "b";
12099
12100             // restore values after effect
12101             var r = this.getFxRestore();
12102             var w = this.getWidth(),
12103                 h = this.getHeight();
12104
12105             var st = this.dom.style;
12106
12107             var after = function(){
12108                 if(o.useDisplay){
12109                     el.setDisplayed(false);
12110                 }else{
12111                     el.hide();
12112                 }
12113
12114                 el.clearOpacity();
12115                 el.setPositioning(r.pos);
12116                 st.width = r.width;
12117                 st.height = r.height;
12118
12119                 el.afterFx(o);
12120             };
12121
12122             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
12123             switch(anchor.toLowerCase()){
12124                 case "t":
12125                     pt.by = [0, -h];
12126                 break;
12127                 case "l":
12128                     pt.by = [-w, 0];
12129                 break;
12130                 case "r":
12131                     pt.by = [w, 0];
12132                 break;
12133                 case "b":
12134                     pt.by = [0, h];
12135                 break;
12136                 case "tl":
12137                     pt.by = [-w, -h];
12138                 break;
12139                 case "bl":
12140                     pt.by = [-w, h];
12141                 break;
12142                 case "br":
12143                     pt.by = [w, h];
12144                 break;
12145                 case "tr":
12146                     pt.by = [w, -h];
12147                 break;
12148             }
12149
12150             arguments.callee.anim = this.fxanim(a,
12151                 o,
12152                 'motion',
12153                 .5,
12154                 "easeOut", after);
12155         });
12156         return this;
12157     },
12158
12159         /**
12160          * Ensures that all effects queued after syncFx is called on the element are
12161          * run concurrently.  This is the opposite of {@link #sequenceFx}.
12162          * @return {Roo.Element} The Element
12163          */
12164     syncFx : function(){
12165         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12166             block : false,
12167             concurrent : true,
12168             stopFx : false
12169         });
12170         return this;
12171     },
12172
12173         /**
12174          * Ensures that all effects queued after sequenceFx is called on the element are
12175          * run in sequence.  This is the opposite of {@link #syncFx}.
12176          * @return {Roo.Element} The Element
12177          */
12178     sequenceFx : function(){
12179         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
12180             block : false,
12181             concurrent : false,
12182             stopFx : false
12183         });
12184         return this;
12185     },
12186
12187         /* @private */
12188     nextFx : function(){
12189         var ef = this.fxQueue[0];
12190         if(ef){
12191             ef.call(this);
12192         }
12193     },
12194
12195         /**
12196          * Returns true if the element has any effects actively running or queued, else returns false.
12197          * @return {Boolean} True if element has active effects, else false
12198          */
12199     hasActiveFx : function(){
12200         return this.fxQueue && this.fxQueue[0];
12201     },
12202
12203         /**
12204          * Stops any running effects and clears the element's internal effects queue if it contains
12205          * any additional effects that haven't started yet.
12206          * @return {Roo.Element} The Element
12207          */
12208     stopFx : function(){
12209         if(this.hasActiveFx()){
12210             var cur = this.fxQueue[0];
12211             if(cur && cur.anim && cur.anim.isAnimated()){
12212                 this.fxQueue = [cur]; // clear out others
12213                 cur.anim.stop(true);
12214             }
12215         }
12216         return this;
12217     },
12218
12219         /* @private */
12220     beforeFx : function(o){
12221         if(this.hasActiveFx() && !o.concurrent){
12222            if(o.stopFx){
12223                this.stopFx();
12224                return true;
12225            }
12226            return false;
12227         }
12228         return true;
12229     },
12230
12231         /**
12232          * Returns true if the element is currently blocking so that no other effect can be queued
12233          * until this effect is finished, else returns false if blocking is not set.  This is commonly
12234          * used to ensure that an effect initiated by a user action runs to completion prior to the
12235          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
12236          * @return {Boolean} True if blocking, else false
12237          */
12238     hasFxBlock : function(){
12239         var q = this.fxQueue;
12240         return q && q[0] && q[0].block;
12241     },
12242
12243         /* @private */
12244     queueFx : function(o, fn){
12245         if(!this.fxQueue){
12246             this.fxQueue = [];
12247         }
12248         if(!this.hasFxBlock()){
12249             Roo.applyIf(o, this.fxDefaults);
12250             if(!o.concurrent){
12251                 var run = this.beforeFx(o);
12252                 fn.block = o.block;
12253                 this.fxQueue.push(fn);
12254                 if(run){
12255                     this.nextFx();
12256                 }
12257             }else{
12258                 fn.call(this);
12259             }
12260         }
12261         return this;
12262     },
12263
12264         /* @private */
12265     fxWrap : function(pos, o, vis){
12266         var wrap;
12267         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
12268             var wrapXY;
12269             if(o.fixPosition){
12270                 wrapXY = this.getXY();
12271             }
12272             var div = document.createElement("div");
12273             div.style.visibility = vis;
12274             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
12275             wrap.setPositioning(pos);
12276             if(wrap.getStyle("position") == "static"){
12277                 wrap.position("relative");
12278             }
12279             this.clearPositioning('auto');
12280             wrap.clip();
12281             wrap.dom.appendChild(this.dom);
12282             if(wrapXY){
12283                 wrap.setXY(wrapXY);
12284             }
12285         }
12286         return wrap;
12287     },
12288
12289         /* @private */
12290     fxUnwrap : function(wrap, pos, o){
12291         this.clearPositioning();
12292         this.setPositioning(pos);
12293         if(!o.wrap){
12294             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
12295             wrap.remove();
12296         }
12297     },
12298
12299         /* @private */
12300     getFxRestore : function(){
12301         var st = this.dom.style;
12302         return {pos: this.getPositioning(), width: st.width, height : st.height};
12303     },
12304
12305         /* @private */
12306     afterFx : function(o){
12307         if(o.afterStyle){
12308             this.applyStyles(o.afterStyle);
12309         }
12310         if(o.afterCls){
12311             this.addClass(o.afterCls);
12312         }
12313         if(o.remove === true){
12314             this.remove();
12315         }
12316         Roo.callback(o.callback, o.scope, [this]);
12317         if(!o.concurrent){
12318             this.fxQueue.shift();
12319             this.nextFx();
12320         }
12321     },
12322
12323         /* @private */
12324     getFxEl : function(){ // support for composite element fx
12325         return Roo.get(this.dom);
12326     },
12327
12328         /* @private */
12329     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12330         animType = animType || 'run';
12331         opt = opt || {};
12332         var anim = Roo.lib.Anim[animType](
12333             this.dom, args,
12334             (opt.duration || defaultDur) || .35,
12335             (opt.easing || defaultEase) || 'easeOut',
12336             function(){
12337                 Roo.callback(cb, this);
12338             },
12339             this
12340         );
12341         opt.anim = anim;
12342         return anim;
12343     }
12344 };
12345
12346 // backwords compat
12347 Roo.Fx.resize = Roo.Fx.scale;
12348
12349 //When included, Roo.Fx is automatically applied to Element so that all basic
12350 //effects are available directly via the Element API
12351 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12352  * Based on:
12353  * Ext JS Library 1.1.1
12354  * Copyright(c) 2006-2007, Ext JS, LLC.
12355  *
12356  * Originally Released Under LGPL - original licence link has changed is not relivant.
12357  *
12358  * Fork - LGPL
12359  * <script type="text/javascript">
12360  */
12361
12362
12363 /**
12364  * @class Roo.CompositeElement
12365  * Standard composite class. Creates a Roo.Element for every element in the collection.
12366  * <br><br>
12367  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12368  * actions will be performed on all the elements in this collection.</b>
12369  * <br><br>
12370  * All methods return <i>this</i> and can be chained.
12371  <pre><code>
12372  var els = Roo.select("#some-el div.some-class", true);
12373  // or select directly from an existing element
12374  var el = Roo.get('some-el');
12375  el.select('div.some-class', true);
12376
12377  els.setWidth(100); // all elements become 100 width
12378  els.hide(true); // all elements fade out and hide
12379  // or
12380  els.setWidth(100).hide(true);
12381  </code></pre>
12382  */
12383 Roo.CompositeElement = function(els){
12384     this.elements = [];
12385     this.addElements(els);
12386 };
12387 Roo.CompositeElement.prototype = {
12388     isComposite: true,
12389     addElements : function(els){
12390         if(!els) {
12391             return this;
12392         }
12393         if(typeof els == "string"){
12394             els = Roo.Element.selectorFunction(els);
12395         }
12396         var yels = this.elements;
12397         var index = yels.length-1;
12398         for(var i = 0, len = els.length; i < len; i++) {
12399                 yels[++index] = Roo.get(els[i]);
12400         }
12401         return this;
12402     },
12403
12404     /**
12405     * Clears this composite and adds the elements returned by the passed selector.
12406     * @param {String/Array} els A string CSS selector, an array of elements or an element
12407     * @return {CompositeElement} this
12408     */
12409     fill : function(els){
12410         this.elements = [];
12411         this.add(els);
12412         return this;
12413     },
12414
12415     /**
12416     * Filters this composite to only elements that match the passed selector.
12417     * @param {String} selector A string CSS selector
12418     * @param {Boolean} inverse return inverse filter (not matches)
12419     * @return {CompositeElement} this
12420     */
12421     filter : function(selector, inverse){
12422         var els = [];
12423         inverse = inverse || false;
12424         this.each(function(el){
12425             var match = inverse ? !el.is(selector) : el.is(selector);
12426             if(match){
12427                 els[els.length] = el.dom;
12428             }
12429         });
12430         this.fill(els);
12431         return this;
12432     },
12433
12434     invoke : function(fn, args){
12435         var els = this.elements;
12436         for(var i = 0, len = els.length; i < len; i++) {
12437                 Roo.Element.prototype[fn].apply(els[i], args);
12438         }
12439         return this;
12440     },
12441     /**
12442     * Adds elements to this composite.
12443     * @param {String/Array} els A string CSS selector, an array of elements or an element
12444     * @return {CompositeElement} this
12445     */
12446     add : function(els){
12447         if(typeof els == "string"){
12448             this.addElements(Roo.Element.selectorFunction(els));
12449         }else if(els.length !== undefined){
12450             this.addElements(els);
12451         }else{
12452             this.addElements([els]);
12453         }
12454         return this;
12455     },
12456     /**
12457     * Calls the passed function passing (el, this, index) for each element in this composite.
12458     * @param {Function} fn The function to call
12459     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12460     * @return {CompositeElement} this
12461     */
12462     each : function(fn, scope){
12463         var els = this.elements;
12464         for(var i = 0, len = els.length; i < len; i++){
12465             if(fn.call(scope || els[i], els[i], this, i) === false) {
12466                 break;
12467             }
12468         }
12469         return this;
12470     },
12471
12472     /**
12473      * Returns the Element object at the specified index
12474      * @param {Number} index
12475      * @return {Roo.Element}
12476      */
12477     item : function(index){
12478         return this.elements[index] || null;
12479     },
12480
12481     /**
12482      * Returns the first Element
12483      * @return {Roo.Element}
12484      */
12485     first : function(){
12486         return this.item(0);
12487     },
12488
12489     /**
12490      * Returns the last Element
12491      * @return {Roo.Element}
12492      */
12493     last : function(){
12494         return this.item(this.elements.length-1);
12495     },
12496
12497     /**
12498      * Returns the number of elements in this composite
12499      * @return Number
12500      */
12501     getCount : function(){
12502         return this.elements.length;
12503     },
12504
12505     /**
12506      * Returns true if this composite contains the passed element
12507      * @return Boolean
12508      */
12509     contains : function(el){
12510         return this.indexOf(el) !== -1;
12511     },
12512
12513     /**
12514      * Returns true if this composite contains the passed element
12515      * @return Boolean
12516      */
12517     indexOf : function(el){
12518         return this.elements.indexOf(Roo.get(el));
12519     },
12520
12521
12522     /**
12523     * Removes the specified element(s).
12524     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12525     * or an array of any of those.
12526     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12527     * @return {CompositeElement} this
12528     */
12529     removeElement : function(el, removeDom){
12530         if(el instanceof Array){
12531             for(var i = 0, len = el.length; i < len; i++){
12532                 this.removeElement(el[i]);
12533             }
12534             return this;
12535         }
12536         var index = typeof el == 'number' ? el : this.indexOf(el);
12537         if(index !== -1){
12538             if(removeDom){
12539                 var d = this.elements[index];
12540                 if(d.dom){
12541                     d.remove();
12542                 }else{
12543                     d.parentNode.removeChild(d);
12544                 }
12545             }
12546             this.elements.splice(index, 1);
12547         }
12548         return this;
12549     },
12550
12551     /**
12552     * Replaces the specified element with the passed element.
12553     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12554     * to replace.
12555     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12556     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12557     * @return {CompositeElement} this
12558     */
12559     replaceElement : function(el, replacement, domReplace){
12560         var index = typeof el == 'number' ? el : this.indexOf(el);
12561         if(index !== -1){
12562             if(domReplace){
12563                 this.elements[index].replaceWith(replacement);
12564             }else{
12565                 this.elements.splice(index, 1, Roo.get(replacement))
12566             }
12567         }
12568         return this;
12569     },
12570
12571     /**
12572      * Removes all elements.
12573      */
12574     clear : function(){
12575         this.elements = [];
12576     }
12577 };
12578 (function(){
12579     Roo.CompositeElement.createCall = function(proto, fnName){
12580         if(!proto[fnName]){
12581             proto[fnName] = function(){
12582                 return this.invoke(fnName, arguments);
12583             };
12584         }
12585     };
12586     for(var fnName in Roo.Element.prototype){
12587         if(typeof Roo.Element.prototype[fnName] == "function"){
12588             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12589         }
12590     };
12591 })();
12592 /*
12593  * Based on:
12594  * Ext JS Library 1.1.1
12595  * Copyright(c) 2006-2007, Ext JS, LLC.
12596  *
12597  * Originally Released Under LGPL - original licence link has changed is not relivant.
12598  *
12599  * Fork - LGPL
12600  * <script type="text/javascript">
12601  */
12602
12603 /**
12604  * @class Roo.CompositeElementLite
12605  * @extends Roo.CompositeElement
12606  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12607  <pre><code>
12608  var els = Roo.select("#some-el div.some-class");
12609  // or select directly from an existing element
12610  var el = Roo.get('some-el');
12611  el.select('div.some-class');
12612
12613  els.setWidth(100); // all elements become 100 width
12614  els.hide(true); // all elements fade out and hide
12615  // or
12616  els.setWidth(100).hide(true);
12617  </code></pre><br><br>
12618  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12619  * actions will be performed on all the elements in this collection.</b>
12620  */
12621 Roo.CompositeElementLite = function(els){
12622     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12623     this.el = new Roo.Element.Flyweight();
12624 };
12625 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12626     addElements : function(els){
12627         if(els){
12628             if(els instanceof Array){
12629                 this.elements = this.elements.concat(els);
12630             }else{
12631                 var yels = this.elements;
12632                 var index = yels.length-1;
12633                 for(var i = 0, len = els.length; i < len; i++) {
12634                     yels[++index] = els[i];
12635                 }
12636             }
12637         }
12638         return this;
12639     },
12640     invoke : function(fn, args){
12641         var els = this.elements;
12642         var el = this.el;
12643         for(var i = 0, len = els.length; i < len; i++) {
12644             el.dom = els[i];
12645                 Roo.Element.prototype[fn].apply(el, args);
12646         }
12647         return this;
12648     },
12649     /**
12650      * Returns a flyweight Element of the dom element object at the specified index
12651      * @param {Number} index
12652      * @return {Roo.Element}
12653      */
12654     item : function(index){
12655         if(!this.elements[index]){
12656             return null;
12657         }
12658         this.el.dom = this.elements[index];
12659         return this.el;
12660     },
12661
12662     // fixes scope with flyweight
12663     addListener : function(eventName, handler, scope, opt){
12664         var els = this.elements;
12665         for(var i = 0, len = els.length; i < len; i++) {
12666             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12667         }
12668         return this;
12669     },
12670
12671     /**
12672     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12673     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12674     * a reference to the dom node, use el.dom.</b>
12675     * @param {Function} fn The function to call
12676     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12677     * @return {CompositeElement} this
12678     */
12679     each : function(fn, scope){
12680         var els = this.elements;
12681         var el = this.el;
12682         for(var i = 0, len = els.length; i < len; i++){
12683             el.dom = els[i];
12684                 if(fn.call(scope || el, el, this, i) === false){
12685                 break;
12686             }
12687         }
12688         return this;
12689     },
12690
12691     indexOf : function(el){
12692         return this.elements.indexOf(Roo.getDom(el));
12693     },
12694
12695     replaceElement : function(el, replacement, domReplace){
12696         var index = typeof el == 'number' ? el : this.indexOf(el);
12697         if(index !== -1){
12698             replacement = Roo.getDom(replacement);
12699             if(domReplace){
12700                 var d = this.elements[index];
12701                 d.parentNode.insertBefore(replacement, d);
12702                 d.parentNode.removeChild(d);
12703             }
12704             this.elements.splice(index, 1, replacement);
12705         }
12706         return this;
12707     }
12708 });
12709 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12710
12711 /*
12712  * Based on:
12713  * Ext JS Library 1.1.1
12714  * Copyright(c) 2006-2007, Ext JS, LLC.
12715  *
12716  * Originally Released Under LGPL - original licence link has changed is not relivant.
12717  *
12718  * Fork - LGPL
12719  * <script type="text/javascript">
12720  */
12721
12722  
12723
12724 /**
12725  * @class Roo.data.Connection
12726  * @extends Roo.util.Observable
12727  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12728  * either to a configured URL, or to a URL specified at request time. 
12729  * 
12730  * Requests made by this class are asynchronous, and will return immediately. No data from
12731  * the server will be available to the statement immediately following the {@link #request} call.
12732  * To process returned data, use a callback in the request options object, or an event listener.
12733  * 
12734  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12735  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12736  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12737  * property and, if present, the IFRAME's XML document as the responseXML property.
12738  * 
12739  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12740  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12741  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12742  * standard DOM methods.
12743  * @constructor
12744  * @param {Object} config a configuration object.
12745  */
12746 Roo.data.Connection = function(config){
12747     Roo.apply(this, config);
12748     this.addEvents({
12749         /**
12750          * @event beforerequest
12751          * Fires before a network request is made to retrieve a data object.
12752          * @param {Connection} conn This Connection object.
12753          * @param {Object} options The options config object passed to the {@link #request} method.
12754          */
12755         "beforerequest" : true,
12756         /**
12757          * @event requestcomplete
12758          * Fires if the request was successfully completed.
12759          * @param {Connection} conn This Connection object.
12760          * @param {Object} response The XHR object containing the response data.
12761          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12762          * @param {Object} options The options config object passed to the {@link #request} method.
12763          */
12764         "requestcomplete" : true,
12765         /**
12766          * @event requestexception
12767          * Fires if an error HTTP status was returned from the server.
12768          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12769          * @param {Connection} conn This Connection object.
12770          * @param {Object} response The XHR object containing the response data.
12771          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12772          * @param {Object} options The options config object passed to the {@link #request} method.
12773          */
12774         "requestexception" : true
12775     });
12776     Roo.data.Connection.superclass.constructor.call(this);
12777 };
12778
12779 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12780     /**
12781      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12782      */
12783     /**
12784      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12785      * extra parameters to each request made by this object. (defaults to undefined)
12786      */
12787     /**
12788      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12789      *  to each request made by this object. (defaults to undefined)
12790      */
12791     /**
12792      * @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)
12793      */
12794     /**
12795      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12796      */
12797     timeout : 30000,
12798     /**
12799      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12800      * @type Boolean
12801      */
12802     autoAbort:false,
12803
12804     /**
12805      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12806      * @type Boolean
12807      */
12808     disableCaching: true,
12809
12810     /**
12811      * Sends an HTTP request to a remote server.
12812      * @param {Object} options An object which may contain the following properties:<ul>
12813      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12814      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12815      * request, a url encoded string or a function to call to get either.</li>
12816      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12817      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12818      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12819      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12820      * <li>options {Object} The parameter to the request call.</li>
12821      * <li>success {Boolean} True if the request succeeded.</li>
12822      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12823      * </ul></li>
12824      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12825      * The callback is passed the following parameters:<ul>
12826      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12827      * <li>options {Object} The parameter to the request call.</li>
12828      * </ul></li>
12829      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12830      * The callback is passed the following parameters:<ul>
12831      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12832      * <li>options {Object} The parameter to the request call.</li>
12833      * </ul></li>
12834      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12835      * for the callback function. Defaults to the browser window.</li>
12836      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12837      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12838      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12839      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12840      * params for the post data. Any params will be appended to the URL.</li>
12841      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12842      * </ul>
12843      * @return {Number} transactionId
12844      */
12845     request : function(o){
12846         if(this.fireEvent("beforerequest", this, o) !== false){
12847             var p = o.params;
12848
12849             if(typeof p == "function"){
12850                 p = p.call(o.scope||window, o);
12851             }
12852             if(typeof p == "object"){
12853                 p = Roo.urlEncode(o.params);
12854             }
12855             if(this.extraParams){
12856                 var extras = Roo.urlEncode(this.extraParams);
12857                 p = p ? (p + '&' + extras) : extras;
12858             }
12859
12860             var url = o.url || this.url;
12861             if(typeof url == 'function'){
12862                 url = url.call(o.scope||window, o);
12863             }
12864
12865             if(o.form){
12866                 var form = Roo.getDom(o.form);
12867                 url = url || form.action;
12868
12869                 var enctype = form.getAttribute("enctype");
12870                 
12871                 if (o.formData) {
12872                     return this.doFormDataUpload(o, url);
12873                 }
12874                 
12875                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12876                     return this.doFormUpload(o, p, url);
12877                 }
12878                 var f = Roo.lib.Ajax.serializeForm(form);
12879                 p = p ? (p + '&' + f) : f;
12880             }
12881             
12882             if (!o.form && o.formData) {
12883                 o.formData = o.formData === true ? new FormData() : o.formData;
12884                 for (var k in o.params) {
12885                     o.formData.append(k,o.params[k]);
12886                 }
12887                     
12888                 return this.doFormDataUpload(o, url);
12889             }
12890             
12891
12892             var hs = o.headers;
12893             if(this.defaultHeaders){
12894                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12895                 if(!o.headers){
12896                     o.headers = hs;
12897                 }
12898             }
12899
12900             var cb = {
12901                 success: this.handleResponse,
12902                 failure: this.handleFailure,
12903                 scope: this,
12904                 argument: {options: o},
12905                 timeout : o.timeout || this.timeout
12906             };
12907
12908             var method = o.method||this.method||(p ? "POST" : "GET");
12909
12910             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12911                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12912             }
12913
12914             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12915                 if(o.autoAbort){
12916                     this.abort();
12917                 }
12918             }else if(this.autoAbort !== false){
12919                 this.abort();
12920             }
12921
12922             if((method == 'GET' && p) || o.xmlData){
12923                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12924                 p = '';
12925             }
12926             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12927             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12928             Roo.lib.Ajax.useDefaultHeader == true;
12929             return this.transId;
12930         }else{
12931             Roo.callback(o.callback, o.scope, [o, null, null]);
12932             return null;
12933         }
12934     },
12935
12936     /**
12937      * Determine whether this object has a request outstanding.
12938      * @param {Number} transactionId (Optional) defaults to the last transaction
12939      * @return {Boolean} True if there is an outstanding request.
12940      */
12941     isLoading : function(transId){
12942         if(transId){
12943             return Roo.lib.Ajax.isCallInProgress(transId);
12944         }else{
12945             return this.transId ? true : false;
12946         }
12947     },
12948
12949     /**
12950      * Aborts any outstanding request.
12951      * @param {Number} transactionId (Optional) defaults to the last transaction
12952      */
12953     abort : function(transId){
12954         if(transId || this.isLoading()){
12955             Roo.lib.Ajax.abort(transId || this.transId);
12956         }
12957     },
12958
12959     // private
12960     handleResponse : function(response){
12961         this.transId = false;
12962         var options = response.argument.options;
12963         response.argument = options ? options.argument : null;
12964         this.fireEvent("requestcomplete", this, response, options);
12965         Roo.callback(options.success, options.scope, [response, options]);
12966         Roo.callback(options.callback, options.scope, [options, true, response]);
12967     },
12968
12969     // private
12970     handleFailure : function(response, e){
12971         this.transId = false;
12972         var options = response.argument.options;
12973         response.argument = options ? options.argument : null;
12974         this.fireEvent("requestexception", this, response, options, e);
12975         Roo.callback(options.failure, options.scope, [response, options]);
12976         Roo.callback(options.callback, options.scope, [options, false, response]);
12977     },
12978
12979     // private
12980     doFormUpload : function(o, ps, url){
12981         var id = Roo.id();
12982         var frame = document.createElement('iframe');
12983         frame.id = id;
12984         frame.name = id;
12985         frame.className = 'x-hidden';
12986         if(Roo.isIE){
12987             frame.src = Roo.SSL_SECURE_URL;
12988         }
12989         document.body.appendChild(frame);
12990
12991         if(Roo.isIE){
12992            document.frames[id].name = id;
12993         }
12994
12995         var form = Roo.getDom(o.form);
12996         form.target = id;
12997         form.method = 'POST';
12998         form.enctype = form.encoding = 'multipart/form-data';
12999         if(url){
13000             form.action = url;
13001         }
13002
13003         var hiddens, hd;
13004         if(ps){ // add dynamic params
13005             hiddens = [];
13006             ps = Roo.urlDecode(ps, false);
13007             for(var k in ps){
13008                 if(ps.hasOwnProperty(k)){
13009                     hd = document.createElement('input');
13010                     hd.type = 'hidden';
13011                     hd.name = k;
13012                     hd.value = ps[k];
13013                     form.appendChild(hd);
13014                     hiddens.push(hd);
13015                 }
13016             }
13017         }
13018
13019         function cb(){
13020             var r = {  // bogus response object
13021                 responseText : '',
13022                 responseXML : null
13023             };
13024
13025             r.argument = o ? o.argument : null;
13026
13027             try { //
13028                 var doc;
13029                 if(Roo.isIE){
13030                     doc = frame.contentWindow.document;
13031                 }else {
13032                     doc = (frame.contentDocument || window.frames[id].document);
13033                 }
13034                 if(doc && doc.body){
13035                     r.responseText = doc.body.innerHTML;
13036                 }
13037                 if(doc && doc.XMLDocument){
13038                     r.responseXML = doc.XMLDocument;
13039                 }else {
13040                     r.responseXML = doc;
13041                 }
13042             }
13043             catch(e) {
13044                 // ignore
13045             }
13046
13047             Roo.EventManager.removeListener(frame, 'load', cb, this);
13048
13049             this.fireEvent("requestcomplete", this, r, o);
13050             Roo.callback(o.success, o.scope, [r, o]);
13051             Roo.callback(o.callback, o.scope, [o, true, r]);
13052
13053             setTimeout(function(){document.body.removeChild(frame);}, 100);
13054         }
13055
13056         Roo.EventManager.on(frame, 'load', cb, this);
13057         form.submit();
13058
13059         if(hiddens){ // remove dynamic params
13060             for(var i = 0, len = hiddens.length; i < len; i++){
13061                 form.removeChild(hiddens[i]);
13062             }
13063         }
13064     },
13065     // this is a 'formdata version???'
13066     
13067     
13068     doFormDataUpload : function(o,  url)
13069     {
13070         var formData;
13071         if (o.form) {
13072             var form =  Roo.getDom(o.form);
13073             form.enctype = form.encoding = 'multipart/form-data';
13074             formData = o.formData === true ? new FormData(form) : o.formData;
13075         } else {
13076             formData = o.formData === true ? new FormData() : o.formData;
13077         }
13078         
13079       
13080         var cb = {
13081             success: this.handleResponse,
13082             failure: this.handleFailure,
13083             scope: this,
13084             argument: {options: o},
13085             timeout : o.timeout || this.timeout
13086         };
13087  
13088         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
13089             if(o.autoAbort){
13090                 this.abort();
13091             }
13092         }else if(this.autoAbort !== false){
13093             this.abort();
13094         }
13095
13096         //Roo.lib.Ajax.defaultPostHeader = null;
13097         Roo.lib.Ajax.useDefaultHeader = false;
13098         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
13099         Roo.lib.Ajax.useDefaultHeader = true;
13100  
13101          
13102     }
13103     
13104 });
13105 /*
13106  * Based on:
13107  * Ext JS Library 1.1.1
13108  * Copyright(c) 2006-2007, Ext JS, LLC.
13109  *
13110  * Originally Released Under LGPL - original licence link has changed is not relivant.
13111  *
13112  * Fork - LGPL
13113  * <script type="text/javascript">
13114  */
13115  
13116 /**
13117  * Global Ajax request class.
13118  * 
13119  * @class Roo.Ajax
13120  * @extends Roo.data.Connection
13121  * @static
13122  * 
13123  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
13124  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
13125  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
13126  * @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)
13127  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
13128  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
13129  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
13130  */
13131 Roo.Ajax = new Roo.data.Connection({
13132     // fix up the docs
13133     /**
13134      * @scope Roo.Ajax
13135      * @type {Boolear} 
13136      */
13137     autoAbort : false,
13138
13139     /**
13140      * Serialize the passed form into a url encoded string
13141      * @scope Roo.Ajax
13142      * @param {String/HTMLElement} form
13143      * @return {String}
13144      */
13145     serializeForm : function(form){
13146         return Roo.lib.Ajax.serializeForm(form);
13147     }
13148 });/*
13149  * Based on:
13150  * Ext JS Library 1.1.1
13151  * Copyright(c) 2006-2007, Ext JS, LLC.
13152  *
13153  * Originally Released Under LGPL - original licence link has changed is not relivant.
13154  *
13155  * Fork - LGPL
13156  * <script type="text/javascript">
13157  */
13158
13159  
13160 /**
13161  * @class Roo.UpdateManager
13162  * @extends Roo.util.Observable
13163  * Provides AJAX-style update for Element object.<br><br>
13164  * Usage:<br>
13165  * <pre><code>
13166  * // Get it from a Roo.Element object
13167  * var el = Roo.get("foo");
13168  * var mgr = el.getUpdateManager();
13169  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
13170  * ...
13171  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
13172  * <br>
13173  * // or directly (returns the same UpdateManager instance)
13174  * var mgr = new Roo.UpdateManager("myElementId");
13175  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
13176  * mgr.on("update", myFcnNeedsToKnow);
13177  * <br>
13178    // short handed call directly from the element object
13179    Roo.get("foo").load({
13180         url: "bar.php",
13181         scripts:true,
13182         params: "for=bar",
13183         text: "Loading Foo..."
13184    });
13185  * </code></pre>
13186  * @constructor
13187  * Create new UpdateManager directly.
13188  * @param {String/HTMLElement/Roo.Element} el The element to update
13189  * @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).
13190  */
13191 Roo.UpdateManager = function(el, forceNew){
13192     el = Roo.get(el);
13193     if(!forceNew && el.updateManager){
13194         return el.updateManager;
13195     }
13196     /**
13197      * The Element object
13198      * @type Roo.Element
13199      */
13200     this.el = el;
13201     /**
13202      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
13203      * @type String
13204      */
13205     this.defaultUrl = null;
13206
13207     this.addEvents({
13208         /**
13209          * @event beforeupdate
13210          * Fired before an update is made, return false from your handler and the update is cancelled.
13211          * @param {Roo.Element} el
13212          * @param {String/Object/Function} url
13213          * @param {String/Object} params
13214          */
13215         "beforeupdate": true,
13216         /**
13217          * @event update
13218          * Fired after successful update is made.
13219          * @param {Roo.Element} el
13220          * @param {Object} oResponseObject The response Object
13221          */
13222         "update": true,
13223         /**
13224          * @event failure
13225          * Fired on update failure.
13226          * @param {Roo.Element} el
13227          * @param {Object} oResponseObject The response Object
13228          */
13229         "failure": true
13230     });
13231     var d = Roo.UpdateManager.defaults;
13232     /**
13233      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
13234      * @type String
13235      */
13236     this.sslBlankUrl = d.sslBlankUrl;
13237     /**
13238      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
13239      * @type Boolean
13240      */
13241     this.disableCaching = d.disableCaching;
13242     /**
13243      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13244      * @type String
13245      */
13246     this.indicatorText = d.indicatorText;
13247     /**
13248      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
13249      * @type String
13250      */
13251     this.showLoadIndicator = d.showLoadIndicator;
13252     /**
13253      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
13254      * @type Number
13255      */
13256     this.timeout = d.timeout;
13257
13258     /**
13259      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
13260      * @type Boolean
13261      */
13262     this.loadScripts = d.loadScripts;
13263
13264     /**
13265      * Transaction object of current executing transaction
13266      */
13267     this.transaction = null;
13268
13269     /**
13270      * @private
13271      */
13272     this.autoRefreshProcId = null;
13273     /**
13274      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
13275      * @type Function
13276      */
13277     this.refreshDelegate = this.refresh.createDelegate(this);
13278     /**
13279      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
13280      * @type Function
13281      */
13282     this.updateDelegate = this.update.createDelegate(this);
13283     /**
13284      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
13285      * @type Function
13286      */
13287     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
13288     /**
13289      * @private
13290      */
13291     this.successDelegate = this.processSuccess.createDelegate(this);
13292     /**
13293      * @private
13294      */
13295     this.failureDelegate = this.processFailure.createDelegate(this);
13296
13297     if(!this.renderer){
13298      /**
13299       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
13300       */
13301     this.renderer = new Roo.UpdateManager.BasicRenderer();
13302     }
13303     
13304     Roo.UpdateManager.superclass.constructor.call(this);
13305 };
13306
13307 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
13308     /**
13309      * Get the Element this UpdateManager is bound to
13310      * @return {Roo.Element} The element
13311      */
13312     getEl : function(){
13313         return this.el;
13314     },
13315     /**
13316      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
13317      * @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:
13318 <pre><code>
13319 um.update({<br/>
13320     url: "your-url.php",<br/>
13321     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13322     callback: yourFunction,<br/>
13323     scope: yourObject, //(optional scope)  <br/>
13324     discardUrl: false, <br/>
13325     nocache: false,<br/>
13326     text: "Loading...",<br/>
13327     timeout: 30,<br/>
13328     scripts: false<br/>
13329 });
13330 </code></pre>
13331      * The only required property is url. The optional properties nocache, text and scripts
13332      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13333      * @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}
13334      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13335      * @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.
13336      */
13337     update : function(url, params, callback, discardUrl){
13338         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13339             var method = this.method,
13340                 cfg;
13341             if(typeof url == "object"){ // must be config object
13342                 cfg = url;
13343                 url = cfg.url;
13344                 params = params || cfg.params;
13345                 callback = callback || cfg.callback;
13346                 discardUrl = discardUrl || cfg.discardUrl;
13347                 if(callback && cfg.scope){
13348                     callback = callback.createDelegate(cfg.scope);
13349                 }
13350                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13351                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13352                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13353                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13354                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13355             }
13356             this.showLoading();
13357             if(!discardUrl){
13358                 this.defaultUrl = url;
13359             }
13360             if(typeof url == "function"){
13361                 url = url.call(this);
13362             }
13363
13364             method = method || (params ? "POST" : "GET");
13365             if(method == "GET"){
13366                 url = this.prepareUrl(url);
13367             }
13368
13369             var o = Roo.apply(cfg ||{}, {
13370                 url : url,
13371                 params: params,
13372                 success: this.successDelegate,
13373                 failure: this.failureDelegate,
13374                 callback: undefined,
13375                 timeout: (this.timeout*1000),
13376                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13377             });
13378             Roo.log("updated manager called with timeout of " + o.timeout);
13379             this.transaction = Roo.Ajax.request(o);
13380         }
13381     },
13382
13383     /**
13384      * 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.
13385      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13386      * @param {String/HTMLElement} form The form Id or form element
13387      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13388      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13389      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13390      */
13391     formUpdate : function(form, url, reset, callback){
13392         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13393             if(typeof url == "function"){
13394                 url = url.call(this);
13395             }
13396             form = Roo.getDom(form);
13397             this.transaction = Roo.Ajax.request({
13398                 form: form,
13399                 url:url,
13400                 success: this.successDelegate,
13401                 failure: this.failureDelegate,
13402                 timeout: (this.timeout*1000),
13403                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13404             });
13405             this.showLoading.defer(1, this);
13406         }
13407     },
13408
13409     /**
13410      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13411      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13412      */
13413     refresh : function(callback){
13414         if(this.defaultUrl == null){
13415             return;
13416         }
13417         this.update(this.defaultUrl, null, callback, true);
13418     },
13419
13420     /**
13421      * Set this element to auto refresh.
13422      * @param {Number} interval How often to update (in seconds).
13423      * @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)
13424      * @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}
13425      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13426      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13427      */
13428     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13429         if(refreshNow){
13430             this.update(url || this.defaultUrl, params, callback, true);
13431         }
13432         if(this.autoRefreshProcId){
13433             clearInterval(this.autoRefreshProcId);
13434         }
13435         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13436     },
13437
13438     /**
13439      * Stop auto refresh on this element.
13440      */
13441      stopAutoRefresh : function(){
13442         if(this.autoRefreshProcId){
13443             clearInterval(this.autoRefreshProcId);
13444             delete this.autoRefreshProcId;
13445         }
13446     },
13447
13448     isAutoRefreshing : function(){
13449        return this.autoRefreshProcId ? true : false;
13450     },
13451     /**
13452      * Called to update the element to "Loading" state. Override to perform custom action.
13453      */
13454     showLoading : function(){
13455         if(this.showLoadIndicator){
13456             this.el.update(this.indicatorText);
13457         }
13458     },
13459
13460     /**
13461      * Adds unique parameter to query string if disableCaching = true
13462      * @private
13463      */
13464     prepareUrl : function(url){
13465         if(this.disableCaching){
13466             var append = "_dc=" + (new Date().getTime());
13467             if(url.indexOf("?") !== -1){
13468                 url += "&" + append;
13469             }else{
13470                 url += "?" + append;
13471             }
13472         }
13473         return url;
13474     },
13475
13476     /**
13477      * @private
13478      */
13479     processSuccess : function(response){
13480         this.transaction = null;
13481         if(response.argument.form && response.argument.reset){
13482             try{ // put in try/catch since some older FF releases had problems with this
13483                 response.argument.form.reset();
13484             }catch(e){}
13485         }
13486         if(this.loadScripts){
13487             this.renderer.render(this.el, response, this,
13488                 this.updateComplete.createDelegate(this, [response]));
13489         }else{
13490             this.renderer.render(this.el, response, this);
13491             this.updateComplete(response);
13492         }
13493     },
13494
13495     updateComplete : function(response){
13496         this.fireEvent("update", this.el, response);
13497         if(typeof response.argument.callback == "function"){
13498             response.argument.callback(this.el, true, response);
13499         }
13500     },
13501
13502     /**
13503      * @private
13504      */
13505     processFailure : function(response){
13506         this.transaction = null;
13507         this.fireEvent("failure", this.el, response);
13508         if(typeof response.argument.callback == "function"){
13509             response.argument.callback(this.el, false, response);
13510         }
13511     },
13512
13513     /**
13514      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13515      * @param {Object} renderer The object implementing the render() method
13516      */
13517     setRenderer : function(renderer){
13518         this.renderer = renderer;
13519     },
13520
13521     getRenderer : function(){
13522        return this.renderer;
13523     },
13524
13525     /**
13526      * Set the defaultUrl used for updates
13527      * @param {String/Function} defaultUrl The url or a function to call to get the url
13528      */
13529     setDefaultUrl : function(defaultUrl){
13530         this.defaultUrl = defaultUrl;
13531     },
13532
13533     /**
13534      * Aborts the executing transaction
13535      */
13536     abort : function(){
13537         if(this.transaction){
13538             Roo.Ajax.abort(this.transaction);
13539         }
13540     },
13541
13542     /**
13543      * Returns true if an update is in progress
13544      * @return {Boolean}
13545      */
13546     isUpdating : function(){
13547         if(this.transaction){
13548             return Roo.Ajax.isLoading(this.transaction);
13549         }
13550         return false;
13551     }
13552 });
13553
13554 /**
13555  * @class Roo.UpdateManager.defaults
13556  * @static (not really - but it helps the doc tool)
13557  * The defaults collection enables customizing the default properties of UpdateManager
13558  */
13559    Roo.UpdateManager.defaults = {
13560        /**
13561          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13562          * @type Number
13563          */
13564          timeout : 30,
13565
13566          /**
13567          * True to process scripts by default (Defaults to false).
13568          * @type Boolean
13569          */
13570         loadScripts : false,
13571
13572         /**
13573         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13574         * @type String
13575         */
13576         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13577         /**
13578          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13579          * @type Boolean
13580          */
13581         disableCaching : false,
13582         /**
13583          * Whether to show indicatorText when loading (Defaults to true).
13584          * @type Boolean
13585          */
13586         showLoadIndicator : true,
13587         /**
13588          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13589          * @type String
13590          */
13591         indicatorText : '<div class="loading-indicator">Loading...</div>'
13592    };
13593
13594 /**
13595  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13596  *Usage:
13597  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13598  * @param {String/HTMLElement/Roo.Element} el The element to update
13599  * @param {String} url The url
13600  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13601  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13602  * @static
13603  * @deprecated
13604  * @member Roo.UpdateManager
13605  */
13606 Roo.UpdateManager.updateElement = function(el, url, params, options){
13607     var um = Roo.get(el, true).getUpdateManager();
13608     Roo.apply(um, options);
13609     um.update(url, params, options ? options.callback : null);
13610 };
13611 // alias for backwards compat
13612 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13613 /**
13614  * @class Roo.UpdateManager.BasicRenderer
13615  * Default Content renderer. Updates the elements innerHTML with the responseText.
13616  */
13617 Roo.UpdateManager.BasicRenderer = function(){};
13618
13619 Roo.UpdateManager.BasicRenderer.prototype = {
13620     /**
13621      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13622      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13623      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13624      * @param {Roo.Element} el The element being rendered
13625      * @param {Object} response The YUI Connect response object
13626      * @param {UpdateManager} updateManager The calling update manager
13627      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13628      */
13629      render : function(el, response, updateManager, callback){
13630         el.update(response.responseText, updateManager.loadScripts, callback);
13631     }
13632 };
13633 /*
13634  * Based on:
13635  * Roo JS
13636  * (c)) Alan Knowles
13637  * Licence : LGPL
13638  */
13639
13640
13641 /**
13642  * @class Roo.DomTemplate
13643  * @extends Roo.Template
13644  * An effort at a dom based template engine..
13645  *
13646  * Similar to XTemplate, except it uses dom parsing to create the template..
13647  *
13648  * Supported features:
13649  *
13650  *  Tags:
13651
13652 <pre><code>
13653       {a_variable} - output encoded.
13654       {a_variable.format:("Y-m-d")} - call a method on the variable
13655       {a_variable:raw} - unencoded output
13656       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13657       {a_variable:this.method_on_template(...)} - call a method on the template object.
13658  
13659 </code></pre>
13660  *  The tpl tag:
13661 <pre><code>
13662         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13663         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13664         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13665         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13666   
13667 </code></pre>
13668  *      
13669  */
13670 Roo.DomTemplate = function()
13671 {
13672      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13673      if (this.html) {
13674         this.compile();
13675      }
13676 };
13677
13678
13679 Roo.extend(Roo.DomTemplate, Roo.Template, {
13680     /**
13681      * id counter for sub templates.
13682      */
13683     id : 0,
13684     /**
13685      * flag to indicate if dom parser is inside a pre,
13686      * it will strip whitespace if not.
13687      */
13688     inPre : false,
13689     
13690     /**
13691      * The various sub templates
13692      */
13693     tpls : false,
13694     
13695     
13696     
13697     /**
13698      *
13699      * basic tag replacing syntax
13700      * WORD:WORD()
13701      *
13702      * // you can fake an object call by doing this
13703      *  x.t:(test,tesT) 
13704      * 
13705      */
13706     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13707     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13708     
13709     iterChild : function (node, method) {
13710         
13711         var oldPre = this.inPre;
13712         if (node.tagName == 'PRE') {
13713             this.inPre = true;
13714         }
13715         for( var i = 0; i < node.childNodes.length; i++) {
13716             method.call(this, node.childNodes[i]);
13717         }
13718         this.inPre = oldPre;
13719     },
13720     
13721     
13722     
13723     /**
13724      * compile the template
13725      *
13726      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13727      *
13728      */
13729     compile: function()
13730     {
13731         var s = this.html;
13732         
13733         // covert the html into DOM...
13734         var doc = false;
13735         var div =false;
13736         try {
13737             doc = document.implementation.createHTMLDocument("");
13738             doc.documentElement.innerHTML =   this.html  ;
13739             div = doc.documentElement;
13740         } catch (e) {
13741             // old IE... - nasty -- it causes all sorts of issues.. with
13742             // images getting pulled from server..
13743             div = document.createElement('div');
13744             div.innerHTML = this.html;
13745         }
13746         //doc.documentElement.innerHTML = htmlBody
13747          
13748         
13749         
13750         this.tpls = [];
13751         var _t = this;
13752         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13753         
13754         var tpls = this.tpls;
13755         
13756         // create a top level template from the snippet..
13757         
13758         //Roo.log(div.innerHTML);
13759         
13760         var tpl = {
13761             uid : 'master',
13762             id : this.id++,
13763             attr : false,
13764             value : false,
13765             body : div.innerHTML,
13766             
13767             forCall : false,
13768             execCall : false,
13769             dom : div,
13770             isTop : true
13771             
13772         };
13773         tpls.unshift(tpl);
13774         
13775         
13776         // compile them...
13777         this.tpls = [];
13778         Roo.each(tpls, function(tp){
13779             this.compileTpl(tp);
13780             this.tpls[tp.id] = tp;
13781         }, this);
13782         
13783         this.master = tpls[0];
13784         return this;
13785         
13786         
13787     },
13788     
13789     compileNode : function(node, istop) {
13790         // test for
13791         //Roo.log(node);
13792         
13793         
13794         // skip anything not a tag..
13795         if (node.nodeType != 1) {
13796             if (node.nodeType == 3 && !this.inPre) {
13797                 // reduce white space..
13798                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13799                 
13800             }
13801             return;
13802         }
13803         
13804         var tpl = {
13805             uid : false,
13806             id : false,
13807             attr : false,
13808             value : false,
13809             body : '',
13810             
13811             forCall : false,
13812             execCall : false,
13813             dom : false,
13814             isTop : istop
13815             
13816             
13817         };
13818         
13819         
13820         switch(true) {
13821             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13822             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13823             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13824             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13825             // no default..
13826         }
13827         
13828         
13829         if (!tpl.attr) {
13830             // just itterate children..
13831             this.iterChild(node,this.compileNode);
13832             return;
13833         }
13834         tpl.uid = this.id++;
13835         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13836         node.removeAttribute('roo-'+ tpl.attr);
13837         if (tpl.attr != 'name') {
13838             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13839             node.parentNode.replaceChild(placeholder,  node);
13840         } else {
13841             
13842             var placeholder =  document.createElement('span');
13843             placeholder.className = 'roo-tpl-' + tpl.value;
13844             node.parentNode.replaceChild(placeholder,  node);
13845         }
13846         
13847         // parent now sees '{domtplXXXX}
13848         this.iterChild(node,this.compileNode);
13849         
13850         // we should now have node body...
13851         var div = document.createElement('div');
13852         div.appendChild(node);
13853         tpl.dom = node;
13854         // this has the unfortunate side effect of converting tagged attributes
13855         // eg. href="{...}" into %7C...%7D
13856         // this has been fixed by searching for those combo's although it's a bit hacky..
13857         
13858         
13859         tpl.body = div.innerHTML;
13860         
13861         
13862          
13863         tpl.id = tpl.uid;
13864         switch(tpl.attr) {
13865             case 'for' :
13866                 switch (tpl.value) {
13867                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13868                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13869                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13870                 }
13871                 break;
13872             
13873             case 'exec':
13874                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13875                 break;
13876             
13877             case 'if':     
13878                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13879                 break;
13880             
13881             case 'name':
13882                 tpl.id  = tpl.value; // replace non characters???
13883                 break;
13884             
13885         }
13886         
13887         
13888         this.tpls.push(tpl);
13889         
13890         
13891         
13892     },
13893     
13894     
13895     
13896     
13897     /**
13898      * Compile a segment of the template into a 'sub-template'
13899      *
13900      * 
13901      * 
13902      *
13903      */
13904     compileTpl : function(tpl)
13905     {
13906         var fm = Roo.util.Format;
13907         var useF = this.disableFormats !== true;
13908         
13909         var sep = Roo.isGecko ? "+\n" : ",\n";
13910         
13911         var undef = function(str) {
13912             Roo.debug && Roo.log("Property not found :"  + str);
13913             return '';
13914         };
13915           
13916         //Roo.log(tpl.body);
13917         
13918         
13919         
13920         var fn = function(m, lbrace, name, format, args)
13921         {
13922             //Roo.log("ARGS");
13923             //Roo.log(arguments);
13924             args = args ? args.replace(/\\'/g,"'") : args;
13925             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13926             if (typeof(format) == 'undefined') {
13927                 format =  'htmlEncode'; 
13928             }
13929             if (format == 'raw' ) {
13930                 format = false;
13931             }
13932             
13933             if(name.substr(0, 6) == 'domtpl'){
13934                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13935             }
13936             
13937             // build an array of options to determine if value is undefined..
13938             
13939             // basically get 'xxxx.yyyy' then do
13940             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13941             //    (function () { Roo.log("Property not found"); return ''; })() :
13942             //    ......
13943             
13944             var udef_ar = [];
13945             var lookfor = '';
13946             Roo.each(name.split('.'), function(st) {
13947                 lookfor += (lookfor.length ? '.': '') + st;
13948                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13949             });
13950             
13951             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13952             
13953             
13954             if(format && useF){
13955                 
13956                 args = args ? ',' + args : "";
13957                  
13958                 if(format.substr(0, 5) != "this."){
13959                     format = "fm." + format + '(';
13960                 }else{
13961                     format = 'this.call("'+ format.substr(5) + '", ';
13962                     args = ", values";
13963                 }
13964                 
13965                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13966             }
13967              
13968             if (args && args.length) {
13969                 // called with xxyx.yuu:(test,test)
13970                 // change to ()
13971                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13972             }
13973             // raw.. - :raw modifier..
13974             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13975             
13976         };
13977         var body;
13978         // branched to use + in gecko and [].join() in others
13979         if(Roo.isGecko){
13980             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13981                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13982                     "';};};";
13983         }else{
13984             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13985             body.push(tpl.body.replace(/(\r\n|\n)/g,
13986                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13987             body.push("'].join('');};};");
13988             body = body.join('');
13989         }
13990         
13991         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13992        
13993         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13994         eval(body);
13995         
13996         return this;
13997     },
13998      
13999     /**
14000      * same as applyTemplate, except it's done to one of the subTemplates
14001      * when using named templates, you can do:
14002      *
14003      * var str = pl.applySubTemplate('your-name', values);
14004      *
14005      * 
14006      * @param {Number} id of the template
14007      * @param {Object} values to apply to template
14008      * @param {Object} parent (normaly the instance of this object)
14009      */
14010     applySubTemplate : function(id, values, parent)
14011     {
14012         
14013         
14014         var t = this.tpls[id];
14015         
14016         
14017         try { 
14018             if(t.ifCall && !t.ifCall.call(this, values, parent)){
14019                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
14020                 return '';
14021             }
14022         } catch(e) {
14023             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
14024             Roo.log(values);
14025           
14026             return '';
14027         }
14028         try { 
14029             
14030             if(t.execCall && t.execCall.call(this, values, parent)){
14031                 return '';
14032             }
14033         } catch(e) {
14034             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14035             Roo.log(values);
14036             return '';
14037         }
14038         
14039         try {
14040             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
14041             parent = t.target ? values : parent;
14042             if(t.forCall && vs instanceof Array){
14043                 var buf = [];
14044                 for(var i = 0, len = vs.length; i < len; i++){
14045                     try {
14046                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
14047                     } catch (e) {
14048                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
14049                         Roo.log(e.body);
14050                         //Roo.log(t.compiled);
14051                         Roo.log(vs[i]);
14052                     }   
14053                 }
14054                 return buf.join('');
14055             }
14056         } catch (e) {
14057             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
14058             Roo.log(values);
14059             return '';
14060         }
14061         try {
14062             return t.compiled.call(this, vs, 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(values);
14068             return '';
14069         }
14070     },
14071
14072    
14073
14074     applyTemplate : function(values){
14075         return this.master.compiled.call(this, values, {});
14076         //var s = this.subs;
14077     },
14078
14079     apply : function(){
14080         return this.applyTemplate.apply(this, arguments);
14081     }
14082
14083  });
14084
14085 Roo.DomTemplate.from = function(el){
14086     el = Roo.getDom(el);
14087     return new Roo.Domtemplate(el.value || el.innerHTML);
14088 };/*
14089  * Based on:
14090  * Ext JS Library 1.1.1
14091  * Copyright(c) 2006-2007, Ext JS, LLC.
14092  *
14093  * Originally Released Under LGPL - original licence link has changed is not relivant.
14094  *
14095  * Fork - LGPL
14096  * <script type="text/javascript">
14097  */
14098
14099 /**
14100  * @class Roo.util.DelayedTask
14101  * Provides a convenient method of performing setTimeout where a new
14102  * timeout cancels the old timeout. An example would be performing validation on a keypress.
14103  * You can use this class to buffer
14104  * the keypress events for a certain number of milliseconds, and perform only if they stop
14105  * for that amount of time.
14106  * @constructor The parameters to this constructor serve as defaults and are not required.
14107  * @param {Function} fn (optional) The default function to timeout
14108  * @param {Object} scope (optional) The default scope of that timeout
14109  * @param {Array} args (optional) The default Array of arguments
14110  */
14111 Roo.util.DelayedTask = function(fn, scope, args){
14112     var id = null, d, t;
14113
14114     var call = function(){
14115         var now = new Date().getTime();
14116         if(now - t >= d){
14117             clearInterval(id);
14118             id = null;
14119             fn.apply(scope, args || []);
14120         }
14121     };
14122     /**
14123      * Cancels any pending timeout and queues a new one
14124      * @param {Number} delay The milliseconds to delay
14125      * @param {Function} newFn (optional) Overrides function passed to constructor
14126      * @param {Object} newScope (optional) Overrides scope passed to constructor
14127      * @param {Array} newArgs (optional) Overrides args passed to constructor
14128      */
14129     this.delay = function(delay, newFn, newScope, newArgs){
14130         if(id && delay != d){
14131             this.cancel();
14132         }
14133         d = delay;
14134         t = new Date().getTime();
14135         fn = newFn || fn;
14136         scope = newScope || scope;
14137         args = newArgs || args;
14138         if(!id){
14139             id = setInterval(call, d);
14140         }
14141     };
14142
14143     /**
14144      * Cancel the last queued timeout
14145      */
14146     this.cancel = function(){
14147         if(id){
14148             clearInterval(id);
14149             id = null;
14150         }
14151     };
14152 };/*
14153  * Based on:
14154  * Ext JS Library 1.1.1
14155  * Copyright(c) 2006-2007, Ext JS, LLC.
14156  *
14157  * Originally Released Under LGPL - original licence link has changed is not relivant.
14158  *
14159  * Fork - LGPL
14160  * <script type="text/javascript">
14161  */
14162 /**
14163  * @class Roo.util.TaskRunner
14164  * Manage background tasks - not sure why this is better that setInterval?
14165  * @static
14166  *
14167  */
14168  
14169 Roo.util.TaskRunner = function(interval){
14170     interval = interval || 10;
14171     var tasks = [], removeQueue = [];
14172     var id = 0;
14173     var running = false;
14174
14175     var stopThread = function(){
14176         running = false;
14177         clearInterval(id);
14178         id = 0;
14179     };
14180
14181     var startThread = function(){
14182         if(!running){
14183             running = true;
14184             id = setInterval(runTasks, interval);
14185         }
14186     };
14187
14188     var removeTask = function(task){
14189         removeQueue.push(task);
14190         if(task.onStop){
14191             task.onStop();
14192         }
14193     };
14194
14195     var runTasks = function(){
14196         if(removeQueue.length > 0){
14197             for(var i = 0, len = removeQueue.length; i < len; i++){
14198                 tasks.remove(removeQueue[i]);
14199             }
14200             removeQueue = [];
14201             if(tasks.length < 1){
14202                 stopThread();
14203                 return;
14204             }
14205         }
14206         var now = new Date().getTime();
14207         for(var i = 0, len = tasks.length; i < len; ++i){
14208             var t = tasks[i];
14209             var itime = now - t.taskRunTime;
14210             if(t.interval <= itime){
14211                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
14212                 t.taskRunTime = now;
14213                 if(rt === false || t.taskRunCount === t.repeat){
14214                     removeTask(t);
14215                     return;
14216                 }
14217             }
14218             if(t.duration && t.duration <= (now - t.taskStartTime)){
14219                 removeTask(t);
14220             }
14221         }
14222     };
14223
14224     /**
14225      * Queues a new task.
14226      * @param {Object} task
14227      *
14228      * Task property : interval = how frequent to run.
14229      * Task object should implement
14230      * function run()
14231      * Task object may implement
14232      * function onStop()
14233      */
14234     this.start = function(task){
14235         tasks.push(task);
14236         task.taskStartTime = new Date().getTime();
14237         task.taskRunTime = 0;
14238         task.taskRunCount = 0;
14239         startThread();
14240         return task;
14241     };
14242     /**
14243      * Stop  new task.
14244      * @param {Object} task
14245      */
14246     this.stop = function(task){
14247         removeTask(task);
14248         return task;
14249     };
14250     /**
14251      * Stop all Tasks
14252      */
14253     this.stopAll = function(){
14254         stopThread();
14255         for(var i = 0, len = tasks.length; i < len; i++){
14256             if(tasks[i].onStop){
14257                 tasks[i].onStop();
14258             }
14259         }
14260         tasks = [];
14261         removeQueue = [];
14262     };
14263 };
14264
14265 Roo.TaskMgr = new Roo.util.TaskRunner();/*
14266  * Based on:
14267  * Ext JS Library 1.1.1
14268  * Copyright(c) 2006-2007, Ext JS, LLC.
14269  *
14270  * Originally Released Under LGPL - original licence link has changed is not relivant.
14271  *
14272  * Fork - LGPL
14273  * <script type="text/javascript">
14274  */
14275
14276  
14277 /**
14278  * @class Roo.util.MixedCollection
14279  * @extends Roo.util.Observable
14280  * A Collection class that maintains both numeric indexes and keys and exposes events.
14281  * @constructor
14282  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
14283  * collection (defaults to false)
14284  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
14285  * and return the key value for that item.  This is used when available to look up the key on items that
14286  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
14287  * equivalent to providing an implementation for the {@link #getKey} method.
14288  */
14289 Roo.util.MixedCollection = function(allowFunctions, keyFn){
14290     this.items = [];
14291     this.map = {};
14292     this.keys = [];
14293     this.length = 0;
14294     this.addEvents({
14295         /**
14296          * @event clear
14297          * Fires when the collection is cleared.
14298          */
14299         "clear" : true,
14300         /**
14301          * @event add
14302          * Fires when an item is added to the collection.
14303          * @param {Number} index The index at which the item was added.
14304          * @param {Object} o The item added.
14305          * @param {String} key The key associated with the added item.
14306          */
14307         "add" : true,
14308         /**
14309          * @event replace
14310          * Fires when an item is replaced in the collection.
14311          * @param {String} key he key associated with the new added.
14312          * @param {Object} old The item being replaced.
14313          * @param {Object} new The new item.
14314          */
14315         "replace" : true,
14316         /**
14317          * @event remove
14318          * Fires when an item is removed from the collection.
14319          * @param {Object} o The item being removed.
14320          * @param {String} key (optional) The key associated with the removed item.
14321          */
14322         "remove" : true,
14323         "sort" : true
14324     });
14325     this.allowFunctions = allowFunctions === true;
14326     if(keyFn){
14327         this.getKey = keyFn;
14328     }
14329     Roo.util.MixedCollection.superclass.constructor.call(this);
14330 };
14331
14332 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14333     allowFunctions : false,
14334     
14335 /**
14336  * Adds an item to the collection.
14337  * @param {String} key The key to associate with the item
14338  * @param {Object} o The item to add.
14339  * @return {Object} The item added.
14340  */
14341     add : function(key, o){
14342         if(arguments.length == 1){
14343             o = arguments[0];
14344             key = this.getKey(o);
14345         }
14346         if(typeof key == "undefined" || key === null){
14347             this.length++;
14348             this.items.push(o);
14349             this.keys.push(null);
14350         }else{
14351             var old = this.map[key];
14352             if(old){
14353                 return this.replace(key, o);
14354             }
14355             this.length++;
14356             this.items.push(o);
14357             this.map[key] = o;
14358             this.keys.push(key);
14359         }
14360         this.fireEvent("add", this.length-1, o, key);
14361         return o;
14362     },
14363        
14364 /**
14365   * MixedCollection has a generic way to fetch keys if you implement getKey.
14366 <pre><code>
14367 // normal way
14368 var mc = new Roo.util.MixedCollection();
14369 mc.add(someEl.dom.id, someEl);
14370 mc.add(otherEl.dom.id, otherEl);
14371 //and so on
14372
14373 // using getKey
14374 var mc = new Roo.util.MixedCollection();
14375 mc.getKey = function(el){
14376    return el.dom.id;
14377 };
14378 mc.add(someEl);
14379 mc.add(otherEl);
14380
14381 // or via the constructor
14382 var mc = new Roo.util.MixedCollection(false, function(el){
14383    return el.dom.id;
14384 });
14385 mc.add(someEl);
14386 mc.add(otherEl);
14387 </code></pre>
14388  * @param o {Object} The item for which to find the key.
14389  * @return {Object} The key for the passed item.
14390  */
14391     getKey : function(o){
14392          return o.id; 
14393     },
14394    
14395 /**
14396  * Replaces an item in the collection.
14397  * @param {String} key The key associated with the item to replace, or the item to replace.
14398  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14399  * @return {Object}  The new item.
14400  */
14401     replace : function(key, o){
14402         if(arguments.length == 1){
14403             o = arguments[0];
14404             key = this.getKey(o);
14405         }
14406         var old = this.item(key);
14407         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14408              return this.add(key, o);
14409         }
14410         var index = this.indexOfKey(key);
14411         this.items[index] = o;
14412         this.map[key] = o;
14413         this.fireEvent("replace", key, old, o);
14414         return o;
14415     },
14416    
14417 /**
14418  * Adds all elements of an Array or an Object to the collection.
14419  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14420  * an Array of values, each of which are added to the collection.
14421  */
14422     addAll : function(objs){
14423         if(arguments.length > 1 || objs instanceof Array){
14424             var args = arguments.length > 1 ? arguments : objs;
14425             for(var i = 0, len = args.length; i < len; i++){
14426                 this.add(args[i]);
14427             }
14428         }else{
14429             for(var key in objs){
14430                 if(this.allowFunctions || typeof objs[key] != "function"){
14431                     this.add(key, objs[key]);
14432                 }
14433             }
14434         }
14435     },
14436    
14437 /**
14438  * Executes the specified function once for every item in the collection, passing each
14439  * item as the first and only parameter. returning false from the function will stop the iteration.
14440  * @param {Function} fn The function to execute for each item.
14441  * @param {Object} scope (optional) The scope in which to execute the function.
14442  */
14443     each : function(fn, scope){
14444         var items = [].concat(this.items); // each safe for removal
14445         for(var i = 0, len = items.length; i < len; i++){
14446             if(fn.call(scope || items[i], items[i], i, len) === false){
14447                 break;
14448             }
14449         }
14450     },
14451    
14452 /**
14453  * Executes the specified function once for every key in the collection, passing each
14454  * key, and its associated item as the first two parameters.
14455  * @param {Function} fn The function to execute for each item.
14456  * @param {Object} scope (optional) The scope in which to execute the function.
14457  */
14458     eachKey : function(fn, scope){
14459         for(var i = 0, len = this.keys.length; i < len; i++){
14460             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14461         }
14462     },
14463    
14464 /**
14465  * Returns the first item in the collection which elicits a true return value from the
14466  * passed selection function.
14467  * @param {Function} fn The selection function to execute for each item.
14468  * @param {Object} scope (optional) The scope in which to execute the function.
14469  * @return {Object} The first item in the collection which returned true from the selection function.
14470  */
14471     find : function(fn, scope){
14472         for(var i = 0, len = this.items.length; i < len; i++){
14473             if(fn.call(scope || window, this.items[i], this.keys[i])){
14474                 return this.items[i];
14475             }
14476         }
14477         return null;
14478     },
14479    
14480 /**
14481  * Inserts an item at the specified index in the collection.
14482  * @param {Number} index The index to insert the item at.
14483  * @param {String} key The key to associate with the new item, or the item itself.
14484  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14485  * @return {Object} The item inserted.
14486  */
14487     insert : function(index, key, o){
14488         if(arguments.length == 2){
14489             o = arguments[1];
14490             key = this.getKey(o);
14491         }
14492         if(index >= this.length){
14493             return this.add(key, o);
14494         }
14495         this.length++;
14496         this.items.splice(index, 0, o);
14497         if(typeof key != "undefined" && key != null){
14498             this.map[key] = o;
14499         }
14500         this.keys.splice(index, 0, key);
14501         this.fireEvent("add", index, o, key);
14502         return o;
14503     },
14504    
14505 /**
14506  * Removed an item from the collection.
14507  * @param {Object} o The item to remove.
14508  * @return {Object} The item removed.
14509  */
14510     remove : function(o){
14511         return this.removeAt(this.indexOf(o));
14512     },
14513    
14514 /**
14515  * Remove an item from a specified index in the collection.
14516  * @param {Number} index The index within the collection of the item to remove.
14517  */
14518     removeAt : function(index){
14519         if(index < this.length && index >= 0){
14520             this.length--;
14521             var o = this.items[index];
14522             this.items.splice(index, 1);
14523             var key = this.keys[index];
14524             if(typeof key != "undefined"){
14525                 delete this.map[key];
14526             }
14527             this.keys.splice(index, 1);
14528             this.fireEvent("remove", o, key);
14529         }
14530     },
14531    
14532 /**
14533  * Removed an item associated with the passed key fom the collection.
14534  * @param {String} key The key of the item to remove.
14535  */
14536     removeKey : function(key){
14537         return this.removeAt(this.indexOfKey(key));
14538     },
14539    
14540 /**
14541  * Returns the number of items in the collection.
14542  * @return {Number} the number of items in the collection.
14543  */
14544     getCount : function(){
14545         return this.length; 
14546     },
14547    
14548 /**
14549  * Returns index within the collection of the passed Object.
14550  * @param {Object} o The item to find the index of.
14551  * @return {Number} index of the item.
14552  */
14553     indexOf : function(o){
14554         if(!this.items.indexOf){
14555             for(var i = 0, len = this.items.length; i < len; i++){
14556                 if(this.items[i] == o) {
14557                     return i;
14558                 }
14559             }
14560             return -1;
14561         }else{
14562             return this.items.indexOf(o);
14563         }
14564     },
14565    
14566 /**
14567  * Returns index within the collection of the passed key.
14568  * @param {String} key The key to find the index of.
14569  * @return {Number} index of the key.
14570  */
14571     indexOfKey : function(key){
14572         if(!this.keys.indexOf){
14573             for(var i = 0, len = this.keys.length; i < len; i++){
14574                 if(this.keys[i] == key) {
14575                     return i;
14576                 }
14577             }
14578             return -1;
14579         }else{
14580             return this.keys.indexOf(key);
14581         }
14582     },
14583    
14584 /**
14585  * Returns the item associated with the passed key OR index. Key has priority over index.
14586  * @param {String/Number} key The key or index of the item.
14587  * @return {Object} The item associated with the passed key.
14588  */
14589     item : function(key){
14590         if (key === 'length') {
14591             return null;
14592         }
14593         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14594         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14595     },
14596     
14597 /**
14598  * Returns the item at the specified index.
14599  * @param {Number} index The index of the item.
14600  * @return {Object}
14601  */
14602     itemAt : function(index){
14603         return this.items[index];
14604     },
14605     
14606 /**
14607  * Returns the item associated with the passed key.
14608  * @param {String/Number} key The key of the item.
14609  * @return {Object} The item associated with the passed key.
14610  */
14611     key : function(key){
14612         return this.map[key];
14613     },
14614    
14615 /**
14616  * Returns true if the collection contains the passed Object as an item.
14617  * @param {Object} o  The Object to look for in the collection.
14618  * @return {Boolean} True if the collection contains the Object as an item.
14619  */
14620     contains : function(o){
14621         return this.indexOf(o) != -1;
14622     },
14623    
14624 /**
14625  * Returns true if the collection contains the passed Object as a key.
14626  * @param {String} key The key to look for in the collection.
14627  * @return {Boolean} True if the collection contains the Object as a key.
14628  */
14629     containsKey : function(key){
14630         return typeof this.map[key] != "undefined";
14631     },
14632    
14633 /**
14634  * Removes all items from the collection.
14635  */
14636     clear : function(){
14637         this.length = 0;
14638         this.items = [];
14639         this.keys = [];
14640         this.map = {};
14641         this.fireEvent("clear");
14642     },
14643    
14644 /**
14645  * Returns the first item in the collection.
14646  * @return {Object} the first item in the collection..
14647  */
14648     first : function(){
14649         return this.items[0]; 
14650     },
14651    
14652 /**
14653  * Returns the last item in the collection.
14654  * @return {Object} the last item in the collection..
14655  */
14656     last : function(){
14657         return this.items[this.length-1];   
14658     },
14659     
14660     _sort : function(property, dir, fn){
14661         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14662         fn = fn || function(a, b){
14663             return a-b;
14664         };
14665         var c = [], k = this.keys, items = this.items;
14666         for(var i = 0, len = items.length; i < len; i++){
14667             c[c.length] = {key: k[i], value: items[i], index: i};
14668         }
14669         c.sort(function(a, b){
14670             var v = fn(a[property], b[property]) * dsc;
14671             if(v == 0){
14672                 v = (a.index < b.index ? -1 : 1);
14673             }
14674             return v;
14675         });
14676         for(var i = 0, len = c.length; i < len; i++){
14677             items[i] = c[i].value;
14678             k[i] = c[i].key;
14679         }
14680         this.fireEvent("sort", this);
14681     },
14682     
14683     /**
14684      * Sorts this collection with the passed comparison function
14685      * @param {String} direction (optional) "ASC" or "DESC"
14686      * @param {Function} fn (optional) comparison function
14687      */
14688     sort : function(dir, fn){
14689         this._sort("value", dir, fn);
14690     },
14691     
14692     /**
14693      * Sorts this collection by keys
14694      * @param {String} direction (optional) "ASC" or "DESC"
14695      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14696      */
14697     keySort : function(dir, fn){
14698         this._sort("key", dir, fn || function(a, b){
14699             return String(a).toUpperCase()-String(b).toUpperCase();
14700         });
14701     },
14702     
14703     /**
14704      * Returns a range of items in this collection
14705      * @param {Number} startIndex (optional) defaults to 0
14706      * @param {Number} endIndex (optional) default to the last item
14707      * @return {Array} An array of items
14708      */
14709     getRange : function(start, end){
14710         var items = this.items;
14711         if(items.length < 1){
14712             return [];
14713         }
14714         start = start || 0;
14715         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14716         var r = [];
14717         if(start <= end){
14718             for(var i = start; i <= end; i++) {
14719                     r[r.length] = items[i];
14720             }
14721         }else{
14722             for(var i = start; i >= end; i--) {
14723                     r[r.length] = items[i];
14724             }
14725         }
14726         return r;
14727     },
14728         
14729     /**
14730      * Filter the <i>objects</i> in this collection by a specific property. 
14731      * Returns a new collection that has been filtered.
14732      * @param {String} property A property on your objects
14733      * @param {String/RegExp} value Either string that the property values 
14734      * should start with or a RegExp to test against the property
14735      * @return {MixedCollection} The new filtered collection
14736      */
14737     filter : function(property, value){
14738         if(!value.exec){ // not a regex
14739             value = String(value);
14740             if(value.length == 0){
14741                 return this.clone();
14742             }
14743             value = new RegExp("^" + Roo.escapeRe(value), "i");
14744         }
14745         return this.filterBy(function(o){
14746             return o && value.test(o[property]);
14747         });
14748         },
14749     
14750     /**
14751      * Filter by a function. * Returns a new collection that has been filtered.
14752      * The passed function will be called with each 
14753      * object in the collection. If the function returns true, the value is included 
14754      * otherwise it is filtered.
14755      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14756      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14757      * @return {MixedCollection} The new filtered collection
14758      */
14759     filterBy : function(fn, scope){
14760         var r = new Roo.util.MixedCollection();
14761         r.getKey = this.getKey;
14762         var k = this.keys, it = this.items;
14763         for(var i = 0, len = it.length; i < len; i++){
14764             if(fn.call(scope||this, it[i], k[i])){
14765                                 r.add(k[i], it[i]);
14766                         }
14767         }
14768         return r;
14769     },
14770     
14771     /**
14772      * Creates a duplicate of this collection
14773      * @return {MixedCollection}
14774      */
14775     clone : function(){
14776         var r = new Roo.util.MixedCollection();
14777         var k = this.keys, it = this.items;
14778         for(var i = 0, len = it.length; i < len; i++){
14779             r.add(k[i], it[i]);
14780         }
14781         r.getKey = this.getKey;
14782         return r;
14783     }
14784 });
14785 /**
14786  * Returns the item associated with the passed key or index.
14787  * @method
14788  * @param {String/Number} key The key or index of the item.
14789  * @return {Object} The item associated with the passed key.
14790  */
14791 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14792  * Based on:
14793  * Ext JS Library 1.1.1
14794  * Copyright(c) 2006-2007, Ext JS, LLC.
14795  *
14796  * Originally Released Under LGPL - original licence link has changed is not relivant.
14797  *
14798  * Fork - LGPL
14799  * <script type="text/javascript">
14800  */
14801 /**
14802  * @class Roo.util.JSON
14803  * Modified version of Douglas Crockford"s json.js that doesn"t
14804  * mess with the Object prototype 
14805  * http://www.json.org/js.html
14806  * @static
14807  */
14808 Roo.util.JSON = new (function(){
14809     var useHasOwn = {}.hasOwnProperty ? true : false;
14810     
14811     // crashes Safari in some instances
14812     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14813     
14814     var pad = function(n) {
14815         return n < 10 ? "0" + n : n;
14816     };
14817     
14818     var m = {
14819         "\b": '\\b',
14820         "\t": '\\t',
14821         "\n": '\\n',
14822         "\f": '\\f',
14823         "\r": '\\r',
14824         '"' : '\\"',
14825         "\\": '\\\\'
14826     };
14827
14828     var encodeString = function(s){
14829         if (/["\\\x00-\x1f]/.test(s)) {
14830             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14831                 var c = m[b];
14832                 if(c){
14833                     return c;
14834                 }
14835                 c = b.charCodeAt();
14836                 return "\\u00" +
14837                     Math.floor(c / 16).toString(16) +
14838                     (c % 16).toString(16);
14839             }) + '"';
14840         }
14841         return '"' + s + '"';
14842     };
14843     
14844     var encodeArray = function(o){
14845         var a = ["["], b, i, l = o.length, v;
14846             for (i = 0; i < l; i += 1) {
14847                 v = o[i];
14848                 switch (typeof v) {
14849                     case "undefined":
14850                     case "function":
14851                     case "unknown":
14852                         break;
14853                     default:
14854                         if (b) {
14855                             a.push(',');
14856                         }
14857                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14858                         b = true;
14859                 }
14860             }
14861             a.push("]");
14862             return a.join("");
14863     };
14864     
14865     var encodeDate = function(o){
14866         return '"' + o.getFullYear() + "-" +
14867                 pad(o.getMonth() + 1) + "-" +
14868                 pad(o.getDate()) + "T" +
14869                 pad(o.getHours()) + ":" +
14870                 pad(o.getMinutes()) + ":" +
14871                 pad(o.getSeconds()) + '"';
14872     };
14873     
14874     /**
14875      * Encodes an Object, Array or other value
14876      * @param {Mixed} o The variable to encode
14877      * @return {String} The JSON string
14878      */
14879     this.encode = function(o)
14880     {
14881         // should this be extended to fully wrap stringify..
14882         
14883         if(typeof o == "undefined" || o === null){
14884             return "null";
14885         }else if(o instanceof Array){
14886             return encodeArray(o);
14887         }else if(o instanceof Date){
14888             return encodeDate(o);
14889         }else if(typeof o == "string"){
14890             return encodeString(o);
14891         }else if(typeof o == "number"){
14892             return isFinite(o) ? String(o) : "null";
14893         }else if(typeof o == "boolean"){
14894             return String(o);
14895         }else {
14896             var a = ["{"], b, i, v;
14897             for (i in o) {
14898                 if(!useHasOwn || o.hasOwnProperty(i)) {
14899                     v = o[i];
14900                     switch (typeof v) {
14901                     case "undefined":
14902                     case "function":
14903                     case "unknown":
14904                         break;
14905                     default:
14906                         if(b){
14907                             a.push(',');
14908                         }
14909                         a.push(this.encode(i), ":",
14910                                 v === null ? "null" : this.encode(v));
14911                         b = true;
14912                     }
14913                 }
14914             }
14915             a.push("}");
14916             return a.join("");
14917         }
14918     };
14919     
14920     /**
14921      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14922      * @param {String} json The JSON string
14923      * @return {Object} The resulting object
14924      */
14925     this.decode = function(json){
14926         
14927         return  /** eval:var:json */ eval("(" + json + ')');
14928     };
14929 })();
14930 /** 
14931  * Shorthand for {@link Roo.util.JSON#encode}
14932  * @member Roo encode 
14933  * @method */
14934 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14935 /** 
14936  * Shorthand for {@link Roo.util.JSON#decode}
14937  * @member Roo decode 
14938  * @method */
14939 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14940 /*
14941  * Based on:
14942  * Ext JS Library 1.1.1
14943  * Copyright(c) 2006-2007, Ext JS, LLC.
14944  *
14945  * Originally Released Under LGPL - original licence link has changed is not relivant.
14946  *
14947  * Fork - LGPL
14948  * <script type="text/javascript">
14949  */
14950  
14951 /**
14952  * @class Roo.util.Format
14953  * Reusable data formatting functions
14954  * @static
14955  */
14956 Roo.util.Format = function(){
14957     var trimRe = /^\s+|\s+$/g;
14958     return {
14959         /**
14960          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14961          * @param {String} value The string to truncate
14962          * @param {Number} length The maximum length to allow before truncating
14963          * @return {String} The converted text
14964          */
14965         ellipsis : function(value, len){
14966             if(value && value.length > len){
14967                 return value.substr(0, len-3)+"...";
14968             }
14969             return value;
14970         },
14971
14972         /**
14973          * Checks a reference and converts it to empty string if it is undefined
14974          * @param {Mixed} value Reference to check
14975          * @return {Mixed} Empty string if converted, otherwise the original value
14976          */
14977         undef : function(value){
14978             return typeof value != "undefined" ? value : "";
14979         },
14980
14981         /**
14982          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14983          * @param {String} value The string to encode
14984          * @return {String} The encoded text
14985          */
14986         htmlEncode : function(value){
14987             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14988         },
14989
14990         /**
14991          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14992          * @param {String} value The string to decode
14993          * @return {String} The decoded text
14994          */
14995         htmlDecode : function(value){
14996             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14997         },
14998
14999         /**
15000          * Trims any whitespace from either side of a string
15001          * @param {String} value The text to trim
15002          * @return {String} The trimmed text
15003          */
15004         trim : function(value){
15005             return String(value).replace(trimRe, "");
15006         },
15007
15008         /**
15009          * Returns a substring from within an original string
15010          * @param {String} value The original text
15011          * @param {Number} start The start index of the substring
15012          * @param {Number} length The length of the substring
15013          * @return {String} The substring
15014          */
15015         substr : function(value, start, length){
15016             return String(value).substr(start, length);
15017         },
15018
15019         /**
15020          * Converts a string to all lower case letters
15021          * @param {String} value The text to convert
15022          * @return {String} The converted text
15023          */
15024         lowercase : function(value){
15025             return String(value).toLowerCase();
15026         },
15027
15028         /**
15029          * Converts a string to all upper case letters
15030          * @param {String} value The text to convert
15031          * @return {String} The converted text
15032          */
15033         uppercase : function(value){
15034             return String(value).toUpperCase();
15035         },
15036
15037         /**
15038          * Converts the first character only of a string to upper case
15039          * @param {String} value The text to convert
15040          * @return {String} The converted text
15041          */
15042         capitalize : function(value){
15043             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
15044         },
15045
15046         // private
15047         call : function(value, fn){
15048             if(arguments.length > 2){
15049                 var args = Array.prototype.slice.call(arguments, 2);
15050                 args.unshift(value);
15051                  
15052                 return /** eval:var:value */  eval(fn).apply(window, args);
15053             }else{
15054                 /** eval:var:value */
15055                 return /** eval:var:value */ eval(fn).call(window, value);
15056             }
15057         },
15058
15059        
15060         /**
15061          * safer version of Math.toFixed..??/
15062          * @param {Number/String} value The numeric value to format
15063          * @param {Number/String} value Decimal places 
15064          * @return {String} The formatted currency string
15065          */
15066         toFixed : function(v, n)
15067         {
15068             // why not use to fixed - precision is buggered???
15069             if (!n) {
15070                 return Math.round(v-0);
15071             }
15072             var fact = Math.pow(10,n+1);
15073             v = (Math.round((v-0)*fact))/fact;
15074             var z = (''+fact).substring(2);
15075             if (v == Math.floor(v)) {
15076                 return Math.floor(v) + '.' + z;
15077             }
15078             
15079             // now just padd decimals..
15080             var ps = String(v).split('.');
15081             var fd = (ps[1] + z);
15082             var r = fd.substring(0,n); 
15083             var rm = fd.substring(n); 
15084             if (rm < 5) {
15085                 return ps[0] + '.' + r;
15086             }
15087             r*=1; // turn it into a number;
15088             r++;
15089             if (String(r).length != n) {
15090                 ps[0]*=1;
15091                 ps[0]++;
15092                 r = String(r).substring(1); // chop the end off.
15093             }
15094             
15095             return ps[0] + '.' + r;
15096              
15097         },
15098         
15099         /**
15100          * Format a number as US currency
15101          * @param {Number/String} value The numeric value to format
15102          * @return {String} The formatted currency string
15103          */
15104         usMoney : function(v){
15105             return '$' + Roo.util.Format.number(v);
15106         },
15107         
15108         /**
15109          * Format a number
15110          * eventually this should probably emulate php's number_format
15111          * @param {Number/String} value The numeric value to format
15112          * @param {Number} decimals number of decimal places
15113          * @param {String} delimiter for thousands (default comma)
15114          * @return {String} The formatted currency string
15115          */
15116         number : function(v, decimals, thousandsDelimiter)
15117         {
15118             // multiply and round.
15119             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
15120             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
15121             
15122             var mul = Math.pow(10, decimals);
15123             var zero = String(mul).substring(1);
15124             v = (Math.round((v-0)*mul))/mul;
15125             
15126             // if it's '0' number.. then
15127             
15128             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
15129             v = String(v);
15130             var ps = v.split('.');
15131             var whole = ps[0];
15132             
15133             var r = /(\d+)(\d{3})/;
15134             // add comma's
15135             
15136             if(thousandsDelimiter.length != 0) {
15137                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
15138             } 
15139             
15140             var sub = ps[1] ?
15141                     // has decimals..
15142                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
15143                     // does not have decimals
15144                     (decimals ? ('.' + zero) : '');
15145             
15146             
15147             return whole + sub ;
15148         },
15149         
15150         /**
15151          * Parse a value into a formatted date using the specified format pattern.
15152          * @param {Mixed} value The value to format
15153          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
15154          * @return {String} The formatted date string
15155          */
15156         date : function(v, format){
15157             if(!v){
15158                 return "";
15159             }
15160             if(!(v instanceof Date)){
15161                 v = new Date(Date.parse(v));
15162             }
15163             return v.dateFormat(format || Roo.util.Format.defaults.date);
15164         },
15165
15166         /**
15167          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
15168          * @param {String} format Any valid date format string
15169          * @return {Function} The date formatting function
15170          */
15171         dateRenderer : function(format){
15172             return function(v){
15173                 return Roo.util.Format.date(v, format);  
15174             };
15175         },
15176
15177         // private
15178         stripTagsRE : /<\/?[^>]+>/gi,
15179         
15180         /**
15181          * Strips all HTML tags
15182          * @param {Mixed} value The text from which to strip tags
15183          * @return {String} The stripped text
15184          */
15185         stripTags : function(v){
15186             return !v ? v : String(v).replace(this.stripTagsRE, "");
15187         },
15188         
15189         /**
15190          * Size in Mb,Gb etc.
15191          * @param {Number} value The number to be formated
15192          * @param {number} decimals how many decimal places
15193          * @return {String} the formated string
15194          */
15195         size : function(value, decimals)
15196         {
15197             var sizes = ['b', 'k', 'M', 'G', 'T'];
15198             if (value == 0) {
15199                 return 0;
15200             }
15201             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
15202             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
15203         }
15204         
15205         
15206         
15207     };
15208 }();
15209 Roo.util.Format.defaults = {
15210     date : 'd/M/Y'
15211 };/*
15212  * Based on:
15213  * Ext JS Library 1.1.1
15214  * Copyright(c) 2006-2007, Ext JS, LLC.
15215  *
15216  * Originally Released Under LGPL - original licence link has changed is not relivant.
15217  *
15218  * Fork - LGPL
15219  * <script type="text/javascript">
15220  */
15221
15222
15223  
15224
15225 /**
15226  * @class Roo.MasterTemplate
15227  * @extends Roo.Template
15228  * Provides a template that can have child templates. The syntax is:
15229 <pre><code>
15230 var t = new Roo.MasterTemplate(
15231         '&lt;select name="{name}"&gt;',
15232                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
15233         '&lt;/select&gt;'
15234 );
15235 t.add('options', {value: 'foo', text: 'bar'});
15236 // or you can add multiple child elements in one shot
15237 t.addAll('options', [
15238     {value: 'foo', text: 'bar'},
15239     {value: 'foo2', text: 'bar2'},
15240     {value: 'foo3', text: 'bar3'}
15241 ]);
15242 // then append, applying the master template values
15243 t.append('my-form', {name: 'my-select'});
15244 </code></pre>
15245 * A name attribute for the child template is not required if you have only one child
15246 * template or you want to refer to them by index.
15247  */
15248 Roo.MasterTemplate = function(){
15249     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
15250     this.originalHtml = this.html;
15251     var st = {};
15252     var m, re = this.subTemplateRe;
15253     re.lastIndex = 0;
15254     var subIndex = 0;
15255     while(m = re.exec(this.html)){
15256         var name = m[1], content = m[2];
15257         st[subIndex] = {
15258             name: name,
15259             index: subIndex,
15260             buffer: [],
15261             tpl : new Roo.Template(content)
15262         };
15263         if(name){
15264             st[name] = st[subIndex];
15265         }
15266         st[subIndex].tpl.compile();
15267         st[subIndex].tpl.call = this.call.createDelegate(this);
15268         subIndex++;
15269     }
15270     this.subCount = subIndex;
15271     this.subs = st;
15272 };
15273 Roo.extend(Roo.MasterTemplate, Roo.Template, {
15274     /**
15275     * The regular expression used to match sub templates
15276     * @type RegExp
15277     * @property
15278     */
15279     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
15280
15281     /**
15282      * Applies the passed values to a child template.
15283      * @param {String/Number} name (optional) The name or index of the child template
15284      * @param {Array/Object} values The values to be applied to the template
15285      * @return {MasterTemplate} this
15286      */
15287      add : function(name, values){
15288         if(arguments.length == 1){
15289             values = arguments[0];
15290             name = 0;
15291         }
15292         var s = this.subs[name];
15293         s.buffer[s.buffer.length] = s.tpl.apply(values);
15294         return this;
15295     },
15296
15297     /**
15298      * Applies all the passed values to a child template.
15299      * @param {String/Number} name (optional) The name or index of the child template
15300      * @param {Array} values The values to be applied to the template, this should be an array of objects.
15301      * @param {Boolean} reset (optional) True to reset the template first
15302      * @return {MasterTemplate} this
15303      */
15304     fill : function(name, values, reset){
15305         var a = arguments;
15306         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
15307             values = a[0];
15308             name = 0;
15309             reset = a[1];
15310         }
15311         if(reset){
15312             this.reset();
15313         }
15314         for(var i = 0, len = values.length; i < len; i++){
15315             this.add(name, values[i]);
15316         }
15317         return this;
15318     },
15319
15320     /**
15321      * Resets the template for reuse
15322      * @return {MasterTemplate} this
15323      */
15324      reset : function(){
15325         var s = this.subs;
15326         for(var i = 0; i < this.subCount; i++){
15327             s[i].buffer = [];
15328         }
15329         return this;
15330     },
15331
15332     applyTemplate : function(values){
15333         var s = this.subs;
15334         var replaceIndex = -1;
15335         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15336             return s[++replaceIndex].buffer.join("");
15337         });
15338         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15339     },
15340
15341     apply : function(){
15342         return this.applyTemplate.apply(this, arguments);
15343     },
15344
15345     compile : function(){return this;}
15346 });
15347
15348 /**
15349  * Alias for fill().
15350  * @method
15351  */
15352 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15353  /**
15354  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15355  * var tpl = Roo.MasterTemplate.from('element-id');
15356  * @param {String/HTMLElement} el
15357  * @param {Object} config
15358  * @static
15359  */
15360 Roo.MasterTemplate.from = function(el, config){
15361     el = Roo.getDom(el);
15362     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15363 };/*
15364  * Based on:
15365  * Ext JS Library 1.1.1
15366  * Copyright(c) 2006-2007, Ext JS, LLC.
15367  *
15368  * Originally Released Under LGPL - original licence link has changed is not relivant.
15369  *
15370  * Fork - LGPL
15371  * <script type="text/javascript">
15372  */
15373
15374  
15375 /**
15376  * @class Roo.util.CSS
15377  * Utility class for manipulating CSS rules
15378  * @static
15379
15380  */
15381 Roo.util.CSS = function(){
15382         var rules = null;
15383         var doc = document;
15384
15385     var camelRe = /(-[a-z])/gi;
15386     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15387
15388    return {
15389    /**
15390     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15391     * tag and appended to the HEAD of the document.
15392     * @param {String|Object} cssText The text containing the css rules
15393     * @param {String} id An id to add to the stylesheet for later removal
15394     * @return {StyleSheet}
15395     */
15396     createStyleSheet : function(cssText, id){
15397         var ss;
15398         var head = doc.getElementsByTagName("head")[0];
15399         var nrules = doc.createElement("style");
15400         nrules.setAttribute("type", "text/css");
15401         if(id){
15402             nrules.setAttribute("id", id);
15403         }
15404         if (typeof(cssText) != 'string') {
15405             // support object maps..
15406             // not sure if this a good idea.. 
15407             // perhaps it should be merged with the general css handling
15408             // and handle js style props.
15409             var cssTextNew = [];
15410             for(var n in cssText) {
15411                 var citems = [];
15412                 for(var k in cssText[n]) {
15413                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15414                 }
15415                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15416                 
15417             }
15418             cssText = cssTextNew.join("\n");
15419             
15420         }
15421        
15422        
15423        if(Roo.isIE){
15424            head.appendChild(nrules);
15425            ss = nrules.styleSheet;
15426            ss.cssText = cssText;
15427        }else{
15428            try{
15429                 nrules.appendChild(doc.createTextNode(cssText));
15430            }catch(e){
15431                nrules.cssText = cssText; 
15432            }
15433            head.appendChild(nrules);
15434            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15435        }
15436        this.cacheStyleSheet(ss);
15437        return ss;
15438    },
15439
15440    /**
15441     * Removes a style or link tag by id
15442     * @param {String} id The id of the tag
15443     */
15444    removeStyleSheet : function(id){
15445        var existing = doc.getElementById(id);
15446        if(existing){
15447            existing.parentNode.removeChild(existing);
15448        }
15449    },
15450
15451    /**
15452     * Dynamically swaps an existing stylesheet reference for a new one
15453     * @param {String} id The id of an existing link tag to remove
15454     * @param {String} url The href of the new stylesheet to include
15455     */
15456    swapStyleSheet : function(id, url){
15457        this.removeStyleSheet(id);
15458        var ss = doc.createElement("link");
15459        ss.setAttribute("rel", "stylesheet");
15460        ss.setAttribute("type", "text/css");
15461        ss.setAttribute("id", id);
15462        ss.setAttribute("href", url);
15463        doc.getElementsByTagName("head")[0].appendChild(ss);
15464    },
15465    
15466    /**
15467     * Refresh the rule cache if you have dynamically added stylesheets
15468     * @return {Object} An object (hash) of rules indexed by selector
15469     */
15470    refreshCache : function(){
15471        return this.getRules(true);
15472    },
15473
15474    // private
15475    cacheStyleSheet : function(stylesheet){
15476        if(!rules){
15477            rules = {};
15478        }
15479        try{// try catch for cross domain access issue
15480            var ssRules = stylesheet.cssRules || stylesheet.rules;
15481            for(var j = ssRules.length-1; j >= 0; --j){
15482                rules[ssRules[j].selectorText] = ssRules[j];
15483            }
15484        }catch(e){}
15485    },
15486    
15487    /**
15488     * Gets all css rules for the document
15489     * @param {Boolean} refreshCache true to refresh the internal cache
15490     * @return {Object} An object (hash) of rules indexed by selector
15491     */
15492    getRules : function(refreshCache){
15493                 if(rules == null || refreshCache){
15494                         rules = {};
15495                         var ds = doc.styleSheets;
15496                         for(var i =0, len = ds.length; i < len; i++){
15497                             try{
15498                         this.cacheStyleSheet(ds[i]);
15499                     }catch(e){} 
15500                 }
15501                 }
15502                 return rules;
15503         },
15504         
15505         /**
15506     * Gets an an individual CSS rule by selector(s)
15507     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15508     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15509     * @return {CSSRule} The CSS rule or null if one is not found
15510     */
15511    getRule : function(selector, refreshCache){
15512                 var rs = this.getRules(refreshCache);
15513                 if(!(selector instanceof Array)){
15514                     return rs[selector];
15515                 }
15516                 for(var i = 0; i < selector.length; i++){
15517                         if(rs[selector[i]]){
15518                                 return rs[selector[i]];
15519                         }
15520                 }
15521                 return null;
15522         },
15523         
15524         
15525         /**
15526     * Updates a rule property
15527     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15528     * @param {String} property The css property
15529     * @param {String} value The new value for the property
15530     * @return {Boolean} true If a rule was found and updated
15531     */
15532    updateRule : function(selector, property, value){
15533                 if(!(selector instanceof Array)){
15534                         var rule = this.getRule(selector);
15535                         if(rule){
15536                                 rule.style[property.replace(camelRe, camelFn)] = value;
15537                                 return true;
15538                         }
15539                 }else{
15540                         for(var i = 0; i < selector.length; i++){
15541                                 if(this.updateRule(selector[i], property, value)){
15542                                         return true;
15543                                 }
15544                         }
15545                 }
15546                 return false;
15547         }
15548    };   
15549 }();/*
15550  * Based on:
15551  * Ext JS Library 1.1.1
15552  * Copyright(c) 2006-2007, Ext JS, LLC.
15553  *
15554  * Originally Released Under LGPL - original licence link has changed is not relivant.
15555  *
15556  * Fork - LGPL
15557  * <script type="text/javascript">
15558  */
15559
15560  
15561
15562 /**
15563  * @class Roo.util.ClickRepeater
15564  * @extends Roo.util.Observable
15565  * 
15566  * A wrapper class which can be applied to any element. Fires a "click" event while the
15567  * mouse is pressed. The interval between firings may be specified in the config but
15568  * defaults to 10 milliseconds.
15569  * 
15570  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15571  * 
15572  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15573  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15574  * Similar to an autorepeat key delay.
15575  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15576  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15577  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15578  *           "interval" and "delay" are ignored. "immediate" is honored.
15579  * @cfg {Boolean} preventDefault True to prevent the default click event
15580  * @cfg {Boolean} stopDefault True to stop the default click event
15581  * 
15582  * @history
15583  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15584  *     2007-02-02 jvs Renamed to ClickRepeater
15585  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15586  *
15587  *  @constructor
15588  * @param {String/HTMLElement/Element} el The element to listen on
15589  * @param {Object} config
15590  **/
15591 Roo.util.ClickRepeater = function(el, config)
15592 {
15593     this.el = Roo.get(el);
15594     this.el.unselectable();
15595
15596     Roo.apply(this, config);
15597
15598     this.addEvents({
15599     /**
15600      * @event mousedown
15601      * Fires when the mouse button is depressed.
15602      * @param {Roo.util.ClickRepeater} this
15603      */
15604         "mousedown" : true,
15605     /**
15606      * @event click
15607      * Fires on a specified interval during the time the element is pressed.
15608      * @param {Roo.util.ClickRepeater} this
15609      */
15610         "click" : true,
15611     /**
15612      * @event mouseup
15613      * Fires when the mouse key is released.
15614      * @param {Roo.util.ClickRepeater} this
15615      */
15616         "mouseup" : true
15617     });
15618
15619     this.el.on("mousedown", this.handleMouseDown, this);
15620     if(this.preventDefault || this.stopDefault){
15621         this.el.on("click", function(e){
15622             if(this.preventDefault){
15623                 e.preventDefault();
15624             }
15625             if(this.stopDefault){
15626                 e.stopEvent();
15627             }
15628         }, this);
15629     }
15630
15631     // allow inline handler
15632     if(this.handler){
15633         this.on("click", this.handler,  this.scope || this);
15634     }
15635
15636     Roo.util.ClickRepeater.superclass.constructor.call(this);
15637 };
15638
15639 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15640     interval : 20,
15641     delay: 250,
15642     preventDefault : true,
15643     stopDefault : false,
15644     timer : 0,
15645
15646     // private
15647     handleMouseDown : function(){
15648         clearTimeout(this.timer);
15649         this.el.blur();
15650         if(this.pressClass){
15651             this.el.addClass(this.pressClass);
15652         }
15653         this.mousedownTime = new Date();
15654
15655         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15656         this.el.on("mouseout", this.handleMouseOut, this);
15657
15658         this.fireEvent("mousedown", this);
15659         this.fireEvent("click", this);
15660         
15661         this.timer = this.click.defer(this.delay || this.interval, this);
15662     },
15663
15664     // private
15665     click : function(){
15666         this.fireEvent("click", this);
15667         this.timer = this.click.defer(this.getInterval(), this);
15668     },
15669
15670     // private
15671     getInterval: function(){
15672         if(!this.accelerate){
15673             return this.interval;
15674         }
15675         var pressTime = this.mousedownTime.getElapsed();
15676         if(pressTime < 500){
15677             return 400;
15678         }else if(pressTime < 1700){
15679             return 320;
15680         }else if(pressTime < 2600){
15681             return 250;
15682         }else if(pressTime < 3500){
15683             return 180;
15684         }else if(pressTime < 4400){
15685             return 140;
15686         }else if(pressTime < 5300){
15687             return 80;
15688         }else if(pressTime < 6200){
15689             return 50;
15690         }else{
15691             return 10;
15692         }
15693     },
15694
15695     // private
15696     handleMouseOut : function(){
15697         clearTimeout(this.timer);
15698         if(this.pressClass){
15699             this.el.removeClass(this.pressClass);
15700         }
15701         this.el.on("mouseover", this.handleMouseReturn, this);
15702     },
15703
15704     // private
15705     handleMouseReturn : function(){
15706         this.el.un("mouseover", this.handleMouseReturn);
15707         if(this.pressClass){
15708             this.el.addClass(this.pressClass);
15709         }
15710         this.click();
15711     },
15712
15713     // private
15714     handleMouseUp : function(){
15715         clearTimeout(this.timer);
15716         this.el.un("mouseover", this.handleMouseReturn);
15717         this.el.un("mouseout", this.handleMouseOut);
15718         Roo.get(document).un("mouseup", this.handleMouseUp);
15719         this.el.removeClass(this.pressClass);
15720         this.fireEvent("mouseup", this);
15721     }
15722 });/**
15723  * @class Roo.util.Clipboard
15724  * @static
15725  * 
15726  * Clipboard UTILS
15727  * 
15728  **/
15729 Roo.util.Clipboard = {
15730     /**
15731      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15732      * @param {String} text to copy to clipboard
15733      */
15734     write : function(text) {
15735         // navigator clipboard api needs a secure context (https)
15736         if (navigator.clipboard && window.isSecureContext) {
15737             // navigator clipboard api method'
15738             navigator.clipboard.writeText(text);
15739             return ;
15740         } 
15741         // text area method
15742         var ta = document.createElement("textarea");
15743         ta.value = text;
15744         // make the textarea out of viewport
15745         ta.style.position = "fixed";
15746         ta.style.left = "-999999px";
15747         ta.style.top = "-999999px";
15748         document.body.appendChild(ta);
15749         ta.focus();
15750         ta.select();
15751         document.execCommand('copy');
15752         (function() {
15753             ta.remove();
15754         }).defer(100);
15755         
15756     }
15757         
15758 }
15759     /*
15760  * Based on:
15761  * Ext JS Library 1.1.1
15762  * Copyright(c) 2006-2007, Ext JS, LLC.
15763  *
15764  * Originally Released Under LGPL - original licence link has changed is not relivant.
15765  *
15766  * Fork - LGPL
15767  * <script type="text/javascript">
15768  */
15769
15770  
15771 /**
15772  * @class Roo.KeyNav
15773  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15774  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15775  * way to implement custom navigation schemes for any UI component.</p>
15776  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15777  * pageUp, pageDown, del, home, end.  Usage:</p>
15778  <pre><code>
15779 var nav = new Roo.KeyNav("my-element", {
15780     "left" : function(e){
15781         this.moveLeft(e.ctrlKey);
15782     },
15783     "right" : function(e){
15784         this.moveRight(e.ctrlKey);
15785     },
15786     "enter" : function(e){
15787         this.save();
15788     },
15789     scope : this
15790 });
15791 </code></pre>
15792  * @constructor
15793  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15794  * @param {Object} config The config
15795  */
15796 Roo.KeyNav = function(el, config){
15797     this.el = Roo.get(el);
15798     Roo.apply(this, config);
15799     if(!this.disabled){
15800         this.disabled = true;
15801         this.enable();
15802     }
15803 };
15804
15805 Roo.KeyNav.prototype = {
15806     /**
15807      * @cfg {Boolean} disabled
15808      * True to disable this KeyNav instance (defaults to false)
15809      */
15810     disabled : false,
15811     /**
15812      * @cfg {String} defaultEventAction
15813      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15814      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15815      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15816      */
15817     defaultEventAction: "stopEvent",
15818     /**
15819      * @cfg {Boolean} forceKeyDown
15820      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15821      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15822      * handle keydown instead of keypress.
15823      */
15824     forceKeyDown : false,
15825
15826     // private
15827     prepareEvent : function(e){
15828         var k = e.getKey();
15829         var h = this.keyToHandler[k];
15830         //if(h && this[h]){
15831         //    e.stopPropagation();
15832         //}
15833         if(Roo.isSafari && h && k >= 37 && k <= 40){
15834             e.stopEvent();
15835         }
15836     },
15837
15838     // private
15839     relay : function(e){
15840         var k = e.getKey();
15841         var h = this.keyToHandler[k];
15842         if(h && this[h]){
15843             if(this.doRelay(e, this[h], h) !== true){
15844                 e[this.defaultEventAction]();
15845             }
15846         }
15847     },
15848
15849     // private
15850     doRelay : function(e, h, hname){
15851         return h.call(this.scope || this, e);
15852     },
15853
15854     // possible handlers
15855     enter : false,
15856     left : false,
15857     right : false,
15858     up : false,
15859     down : false,
15860     tab : false,
15861     esc : false,
15862     pageUp : false,
15863     pageDown : false,
15864     del : false,
15865     home : false,
15866     end : false,
15867
15868     // quick lookup hash
15869     keyToHandler : {
15870         37 : "left",
15871         39 : "right",
15872         38 : "up",
15873         40 : "down",
15874         33 : "pageUp",
15875         34 : "pageDown",
15876         46 : "del",
15877         36 : "home",
15878         35 : "end",
15879         13 : "enter",
15880         27 : "esc",
15881         9  : "tab"
15882     },
15883
15884         /**
15885          * Enable this KeyNav
15886          */
15887         enable: function(){
15888                 if(this.disabled){
15889             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15890             // the EventObject will normalize Safari automatically
15891             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15892                 this.el.on("keydown", this.relay,  this);
15893             }else{
15894                 this.el.on("keydown", this.prepareEvent,  this);
15895                 this.el.on("keypress", this.relay,  this);
15896             }
15897                     this.disabled = false;
15898                 }
15899         },
15900
15901         /**
15902          * Disable this KeyNav
15903          */
15904         disable: function(){
15905                 if(!this.disabled){
15906                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15907                 this.el.un("keydown", this.relay);
15908             }else{
15909                 this.el.un("keydown", this.prepareEvent);
15910                 this.el.un("keypress", this.relay);
15911             }
15912                     this.disabled = true;
15913                 }
15914         }
15915 };/*
15916  * Based on:
15917  * Ext JS Library 1.1.1
15918  * Copyright(c) 2006-2007, Ext JS, LLC.
15919  *
15920  * Originally Released Under LGPL - original licence link has changed is not relivant.
15921  *
15922  * Fork - LGPL
15923  * <script type="text/javascript">
15924  */
15925
15926  
15927 /**
15928  * @class Roo.KeyMap
15929  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15930  * The constructor accepts the same config object as defined by {@link #addBinding}.
15931  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15932  * combination it will call the function with this signature (if the match is a multi-key
15933  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15934  * A KeyMap can also handle a string representation of keys.<br />
15935  * Usage:
15936  <pre><code>
15937 // map one key by key code
15938 var map = new Roo.KeyMap("my-element", {
15939     key: 13, // or Roo.EventObject.ENTER
15940     fn: myHandler,
15941     scope: myObject
15942 });
15943
15944 // map multiple keys to one action by string
15945 var map = new Roo.KeyMap("my-element", {
15946     key: "a\r\n\t",
15947     fn: myHandler,
15948     scope: myObject
15949 });
15950
15951 // map multiple keys to multiple actions by strings and array of codes
15952 var map = new Roo.KeyMap("my-element", [
15953     {
15954         key: [10,13],
15955         fn: function(){ alert("Return was pressed"); }
15956     }, {
15957         key: "abc",
15958         fn: function(){ alert('a, b or c was pressed'); }
15959     }, {
15960         key: "\t",
15961         ctrl:true,
15962         shift:true,
15963         fn: function(){ alert('Control + shift + tab was pressed.'); }
15964     }
15965 ]);
15966 </code></pre>
15967  * <b>Note: A KeyMap starts enabled</b>
15968  * @constructor
15969  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15970  * @param {Object} config The config (see {@link #addBinding})
15971  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15972  */
15973 Roo.KeyMap = function(el, config, eventName){
15974     this.el  = Roo.get(el);
15975     this.eventName = eventName || "keydown";
15976     this.bindings = [];
15977     if(config){
15978         this.addBinding(config);
15979     }
15980     this.enable();
15981 };
15982
15983 Roo.KeyMap.prototype = {
15984     /**
15985      * True to stop the event from bubbling and prevent the default browser action if the
15986      * key was handled by the KeyMap (defaults to false)
15987      * @type Boolean
15988      */
15989     stopEvent : false,
15990
15991     /**
15992      * Add a new binding to this KeyMap. The following config object properties are supported:
15993      * <pre>
15994 Property    Type             Description
15995 ----------  ---------------  ----------------------------------------------------------------------
15996 key         String/Array     A single keycode or an array of keycodes to handle
15997 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15998 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15999 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
16000 fn          Function         The function to call when KeyMap finds the expected key combination
16001 scope       Object           The scope of the callback function
16002 </pre>
16003      *
16004      * Usage:
16005      * <pre><code>
16006 // Create a KeyMap
16007 var map = new Roo.KeyMap(document, {
16008     key: Roo.EventObject.ENTER,
16009     fn: handleKey,
16010     scope: this
16011 });
16012
16013 //Add a new binding to the existing KeyMap later
16014 map.addBinding({
16015     key: 'abc',
16016     shift: true,
16017     fn: handleKey,
16018     scope: this
16019 });
16020 </code></pre>
16021      * @param {Object/Array} config A single KeyMap config or an array of configs
16022      */
16023         addBinding : function(config){
16024         if(config instanceof Array){
16025             for(var i = 0, len = config.length; i < len; i++){
16026                 this.addBinding(config[i]);
16027             }
16028             return;
16029         }
16030         var keyCode = config.key,
16031             shift = config.shift, 
16032             ctrl = config.ctrl, 
16033             alt = config.alt,
16034             fn = config.fn,
16035             scope = config.scope;
16036         if(typeof keyCode == "string"){
16037             var ks = [];
16038             var keyString = keyCode.toUpperCase();
16039             for(var j = 0, len = keyString.length; j < len; j++){
16040                 ks.push(keyString.charCodeAt(j));
16041             }
16042             keyCode = ks;
16043         }
16044         var keyArray = keyCode instanceof Array;
16045         var handler = function(e){
16046             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
16047                 var k = e.getKey();
16048                 if(keyArray){
16049                     for(var i = 0, len = keyCode.length; i < len; i++){
16050                         if(keyCode[i] == k){
16051                           if(this.stopEvent){
16052                               e.stopEvent();
16053                           }
16054                           fn.call(scope || window, k, e);
16055                           return;
16056                         }
16057                     }
16058                 }else{
16059                     if(k == keyCode){
16060                         if(this.stopEvent){
16061                            e.stopEvent();
16062                         }
16063                         fn.call(scope || window, k, e);
16064                     }
16065                 }
16066             }
16067         };
16068         this.bindings.push(handler);  
16069         },
16070
16071     /**
16072      * Shorthand for adding a single key listener
16073      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
16074      * following options:
16075      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
16076      * @param {Function} fn The function to call
16077      * @param {Object} scope (optional) The scope of the function
16078      */
16079     on : function(key, fn, scope){
16080         var keyCode, shift, ctrl, alt;
16081         if(typeof key == "object" && !(key instanceof Array)){
16082             keyCode = key.key;
16083             shift = key.shift;
16084             ctrl = key.ctrl;
16085             alt = key.alt;
16086         }else{
16087             keyCode = key;
16088         }
16089         this.addBinding({
16090             key: keyCode,
16091             shift: shift,
16092             ctrl: ctrl,
16093             alt: alt,
16094             fn: fn,
16095             scope: scope
16096         })
16097     },
16098
16099     // private
16100     handleKeyDown : function(e){
16101             if(this.enabled){ //just in case
16102             var b = this.bindings;
16103             for(var i = 0, len = b.length; i < len; i++){
16104                 b[i].call(this, e);
16105             }
16106             }
16107         },
16108         
16109         /**
16110          * Returns true if this KeyMap is enabled
16111          * @return {Boolean} 
16112          */
16113         isEnabled : function(){
16114             return this.enabled;  
16115         },
16116         
16117         /**
16118          * Enables this KeyMap
16119          */
16120         enable: function(){
16121                 if(!this.enabled){
16122                     this.el.on(this.eventName, this.handleKeyDown, this);
16123                     this.enabled = true;
16124                 }
16125         },
16126
16127         /**
16128          * Disable this KeyMap
16129          */
16130         disable: function(){
16131                 if(this.enabled){
16132                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
16133                     this.enabled = false;
16134                 }
16135         }
16136 };/*
16137  * Based on:
16138  * Ext JS Library 1.1.1
16139  * Copyright(c) 2006-2007, Ext JS, LLC.
16140  *
16141  * Originally Released Under LGPL - original licence link has changed is not relivant.
16142  *
16143  * Fork - LGPL
16144  * <script type="text/javascript">
16145  */
16146
16147  
16148 /**
16149  * @class Roo.util.TextMetrics
16150  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
16151  * wide, in pixels, a given block of text will be.
16152  * @static
16153  */
16154 Roo.util.TextMetrics = function(){
16155     var shared;
16156     return {
16157         /**
16158          * Measures the size of the specified text
16159          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
16160          * that can affect the size of the rendered text
16161          * @param {String} text The text to measure
16162          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16163          * in order to accurately measure the text height
16164          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16165          */
16166         measure : function(el, text, fixedWidth){
16167             if(!shared){
16168                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
16169             }
16170             shared.bind(el);
16171             shared.setFixedWidth(fixedWidth || 'auto');
16172             return shared.getSize(text);
16173         },
16174
16175         /**
16176          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
16177          * the overhead of multiple calls to initialize the style properties on each measurement.
16178          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
16179          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
16180          * in order to accurately measure the text height
16181          * @return {Roo.util.TextMetrics.Instance} instance The new instance
16182          */
16183         createInstance : function(el, fixedWidth){
16184             return Roo.util.TextMetrics.Instance(el, fixedWidth);
16185         }
16186     };
16187 }();
16188
16189 /**
16190  * @class Roo.util.TextMetrics.Instance
16191  * Instance of  TextMetrics Calcuation
16192  * @constructor
16193  * Create a new TextMetrics Instance
16194  * @param {Object} bindto
16195  * @param {Boolean} fixedWidth
16196  */
16197
16198 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
16199 {
16200     var ml = new Roo.Element(document.createElement('div'));
16201     document.body.appendChild(ml.dom);
16202     ml.position('absolute');
16203     ml.setLeftTop(-1000, -1000);
16204     ml.hide();
16205
16206     if(fixedWidth){
16207         ml.setWidth(fixedWidth);
16208     }
16209      
16210     var instance = {
16211         /**
16212          * Returns the size of the specified text based on the internal element's style and width properties
16213          * @param {String} text The text to measure
16214          * @return {Object} An object containing the text's size {width: (width), height: (height)}
16215          */
16216         getSize : function(text){
16217             ml.update(text);
16218             var s = ml.getSize();
16219             ml.update('');
16220             return s;
16221         },
16222
16223         /**
16224          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
16225          * that can affect the size of the rendered text
16226          * @param {String/HTMLElement} el The element, dom node or id
16227          */
16228         bind : function(el){
16229             ml.setStyle(
16230                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
16231             );
16232         },
16233
16234         /**
16235          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
16236          * to set a fixed width in order to accurately measure the text height.
16237          * @param {Number} width The width to set on the element
16238          */
16239         setFixedWidth : function(width){
16240             ml.setWidth(width);
16241         },
16242
16243         /**
16244          * Returns the measured width of the specified text
16245          * @param {String} text The text to measure
16246          * @return {Number} width The width in pixels
16247          */
16248         getWidth : function(text){
16249             ml.dom.style.width = 'auto';
16250             return this.getSize(text).width;
16251         },
16252
16253         /**
16254          * Returns the measured height of the specified text.  For multiline text, be sure to call
16255          * {@link #setFixedWidth} if necessary.
16256          * @param {String} text The text to measure
16257          * @return {Number} height The height in pixels
16258          */
16259         getHeight : function(text){
16260             return this.getSize(text).height;
16261         }
16262     };
16263
16264     instance.bind(bindTo);
16265
16266     return instance;
16267 };
16268
16269 // backwards compat
16270 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
16271  * Based on:
16272  * Ext JS Library 1.1.1
16273  * Copyright(c) 2006-2007, Ext JS, LLC.
16274  *
16275  * Originally Released Under LGPL - original licence link has changed is not relivant.
16276  *
16277  * Fork - LGPL
16278  * <script type="text/javascript">
16279  */
16280
16281 /**
16282  * @class Roo.state.Provider
16283  * Abstract base class for state provider implementations. This class provides methods
16284  * for encoding and decoding <b>typed</b> variables including dates and defines the 
16285  * Provider interface.
16286  */
16287 Roo.state.Provider = function(){
16288     /**
16289      * @event statechange
16290      * Fires when a state change occurs.
16291      * @param {Provider} this This state provider
16292      * @param {String} key The state key which was changed
16293      * @param {String} value The encoded value for the state
16294      */
16295     this.addEvents({
16296         "statechange": true
16297     });
16298     this.state = {};
16299     Roo.state.Provider.superclass.constructor.call(this);
16300 };
16301 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
16302     /**
16303      * Returns the current value for a key
16304      * @param {String} name The key name
16305      * @param {Mixed} defaultValue A default value to return if the key's value is not found
16306      * @return {Mixed} The state data
16307      */
16308     get : function(name, defaultValue){
16309         return typeof this.state[name] == "undefined" ?
16310             defaultValue : this.state[name];
16311     },
16312     
16313     /**
16314      * Clears a value from the state
16315      * @param {String} name The key name
16316      */
16317     clear : function(name){
16318         delete this.state[name];
16319         this.fireEvent("statechange", this, name, null);
16320     },
16321     
16322     /**
16323      * Sets the value for a key
16324      * @param {String} name The key name
16325      * @param {Mixed} value The value to set
16326      */
16327     set : function(name, value){
16328         this.state[name] = value;
16329         this.fireEvent("statechange", this, name, value);
16330     },
16331     
16332     /**
16333      * Decodes a string previously encoded with {@link #encodeValue}.
16334      * @param {String} value The value to decode
16335      * @return {Mixed} The decoded value
16336      */
16337     decodeValue : function(cookie){
16338         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16339         var matches = re.exec(unescape(cookie));
16340         if(!matches || !matches[1]) {
16341             return; // non state cookie
16342         }
16343         var type = matches[1];
16344         var v = matches[2];
16345         switch(type){
16346             case "n":
16347                 return parseFloat(v);
16348             case "d":
16349                 return new Date(Date.parse(v));
16350             case "b":
16351                 return (v == "1");
16352             case "a":
16353                 var all = [];
16354                 var values = v.split("^");
16355                 for(var i = 0, len = values.length; i < len; i++){
16356                     all.push(this.decodeValue(values[i]));
16357                 }
16358                 return all;
16359            case "o":
16360                 var all = {};
16361                 var values = v.split("^");
16362                 for(var i = 0, len = values.length; i < len; i++){
16363                     var kv = values[i].split("=");
16364                     all[kv[0]] = this.decodeValue(kv[1]);
16365                 }
16366                 return all;
16367            default:
16368                 return v;
16369         }
16370     },
16371     
16372     /**
16373      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16374      * @param {Mixed} value The value to encode
16375      * @return {String} The encoded value
16376      */
16377     encodeValue : function(v){
16378         var enc;
16379         if(typeof v == "number"){
16380             enc = "n:" + v;
16381         }else if(typeof v == "boolean"){
16382             enc = "b:" + (v ? "1" : "0");
16383         }else if(v instanceof Date){
16384             enc = "d:" + v.toGMTString();
16385         }else if(v instanceof Array){
16386             var flat = "";
16387             for(var i = 0, len = v.length; i < len; i++){
16388                 flat += this.encodeValue(v[i]);
16389                 if(i != len-1) {
16390                     flat += "^";
16391                 }
16392             }
16393             enc = "a:" + flat;
16394         }else if(typeof v == "object"){
16395             var flat = "";
16396             for(var key in v){
16397                 if(typeof v[key] != "function"){
16398                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16399                 }
16400             }
16401             enc = "o:" + flat.substring(0, flat.length-1);
16402         }else{
16403             enc = "s:" + v;
16404         }
16405         return escape(enc);        
16406     }
16407 });
16408
16409 /*
16410  * Based on:
16411  * Ext JS Library 1.1.1
16412  * Copyright(c) 2006-2007, Ext JS, LLC.
16413  *
16414  * Originally Released Under LGPL - original licence link has changed is not relivant.
16415  *
16416  * Fork - LGPL
16417  * <script type="text/javascript">
16418  */
16419 /**
16420  * @class Roo.state.Manager
16421  * This is the global state manager. By default all components that are "state aware" check this class
16422  * for state information if you don't pass them a custom state provider. In order for this class
16423  * to be useful, it must be initialized with a provider when your application initializes.
16424  <pre><code>
16425 // in your initialization function
16426 init : function(){
16427    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16428    ...
16429    // supposed you have a {@link Roo.BorderLayout}
16430    var layout = new Roo.BorderLayout(...);
16431    layout.restoreState();
16432    // or a {Roo.BasicDialog}
16433    var dialog = new Roo.BasicDialog(...);
16434    dialog.restoreState();
16435  </code></pre>
16436  * @static
16437  */
16438 Roo.state.Manager = function(){
16439     var provider = new Roo.state.Provider();
16440     
16441     return {
16442         /**
16443          * Configures the default state provider for your application
16444          * @param {Provider} stateProvider The state provider to set
16445          */
16446         setProvider : function(stateProvider){
16447             provider = stateProvider;
16448         },
16449         
16450         /**
16451          * Returns the current value for a key
16452          * @param {String} name The key name
16453          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16454          * @return {Mixed} The state data
16455          */
16456         get : function(key, defaultValue){
16457             return provider.get(key, defaultValue);
16458         },
16459         
16460         /**
16461          * Sets the value for a key
16462          * @param {String} name The key name
16463          * @param {Mixed} value The state data
16464          */
16465          set : function(key, value){
16466             provider.set(key, value);
16467         },
16468         
16469         /**
16470          * Clears a value from the state
16471          * @param {String} name The key name
16472          */
16473         clear : function(key){
16474             provider.clear(key);
16475         },
16476         
16477         /**
16478          * Gets the currently configured state provider
16479          * @return {Provider} The state provider
16480          */
16481         getProvider : function(){
16482             return provider;
16483         }
16484     };
16485 }();
16486 /*
16487  * Based on:
16488  * Ext JS Library 1.1.1
16489  * Copyright(c) 2006-2007, Ext JS, LLC.
16490  *
16491  * Originally Released Under LGPL - original licence link has changed is not relivant.
16492  *
16493  * Fork - LGPL
16494  * <script type="text/javascript">
16495  */
16496 /**
16497  * @class Roo.state.CookieProvider
16498  * @extends Roo.state.Provider
16499  * The default Provider implementation which saves state via cookies.
16500  * <br />Usage:
16501  <pre><code>
16502    var cp = new Roo.state.CookieProvider({
16503        path: "/cgi-bin/",
16504        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16505        domain: "roojs.com"
16506    })
16507    Roo.state.Manager.setProvider(cp);
16508  </code></pre>
16509  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16510  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16511  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16512  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16513  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16514  * domain the page is running on including the 'www' like 'www.roojs.com')
16515  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16516  * @constructor
16517  * Create a new CookieProvider
16518  * @param {Object} config The configuration object
16519  */
16520 Roo.state.CookieProvider = function(config){
16521     Roo.state.CookieProvider.superclass.constructor.call(this);
16522     this.path = "/";
16523     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16524     this.domain = null;
16525     this.secure = false;
16526     Roo.apply(this, config);
16527     this.state = this.readCookies();
16528 };
16529
16530 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16531     // private
16532     set : function(name, value){
16533         if(typeof value == "undefined" || value === null){
16534             this.clear(name);
16535             return;
16536         }
16537         this.setCookie(name, value);
16538         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16539     },
16540
16541     // private
16542     clear : function(name){
16543         this.clearCookie(name);
16544         Roo.state.CookieProvider.superclass.clear.call(this, name);
16545     },
16546
16547     // private
16548     readCookies : function(){
16549         var cookies = {};
16550         var c = document.cookie + ";";
16551         var re = /\s?(.*?)=(.*?);/g;
16552         var matches;
16553         while((matches = re.exec(c)) != null){
16554             var name = matches[1];
16555             var value = matches[2];
16556             if(name && name.substring(0,3) == "ys-"){
16557                 cookies[name.substr(3)] = this.decodeValue(value);
16558             }
16559         }
16560         return cookies;
16561     },
16562
16563     // private
16564     setCookie : function(name, value){
16565         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16566            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16567            ((this.path == null) ? "" : ("; path=" + this.path)) +
16568            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16569            ((this.secure == true) ? "; secure" : "");
16570     },
16571
16572     // private
16573     clearCookie : function(name){
16574         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16575            ((this.path == null) ? "" : ("; path=" + this.path)) +
16576            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16577            ((this.secure == true) ? "; secure" : "");
16578     }
16579 });/*
16580  * Based on:
16581  * Ext JS Library 1.1.1
16582  * Copyright(c) 2006-2007, Ext JS, LLC.
16583  *
16584  * Originally Released Under LGPL - original licence link has changed is not relivant.
16585  *
16586  * Fork - LGPL
16587  * <script type="text/javascript">
16588  */
16589  
16590
16591 /**
16592  * @class Roo.ComponentMgr
16593  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16594  * @static
16595  */
16596 Roo.ComponentMgr = function(){
16597     var all = new Roo.util.MixedCollection();
16598
16599     return {
16600         /**
16601          * Registers a component.
16602          * @param {Roo.Component} c The component
16603          */
16604         register : function(c){
16605             all.add(c);
16606         },
16607
16608         /**
16609          * Unregisters a component.
16610          * @param {Roo.Component} c The component
16611          */
16612         unregister : function(c){
16613             all.remove(c);
16614         },
16615
16616         /**
16617          * Returns a component by id
16618          * @param {String} id The component id
16619          */
16620         get : function(id){
16621             return all.get(id);
16622         },
16623
16624         /**
16625          * Registers a function that will be called when a specified component is added to ComponentMgr
16626          * @param {String} id The component id
16627          * @param {Funtction} fn The callback function
16628          * @param {Object} scope The scope of the callback
16629          */
16630         onAvailable : function(id, fn, scope){
16631             all.on("add", function(index, o){
16632                 if(o.id == id){
16633                     fn.call(scope || o, o);
16634                     all.un("add", fn, scope);
16635                 }
16636             });
16637         }
16638     };
16639 }();/*
16640  * Based on:
16641  * Ext JS Library 1.1.1
16642  * Copyright(c) 2006-2007, Ext JS, LLC.
16643  *
16644  * Originally Released Under LGPL - original licence link has changed is not relivant.
16645  *
16646  * Fork - LGPL
16647  * <script type="text/javascript">
16648  */
16649  
16650 /**
16651  * @class Roo.Component
16652  * @extends Roo.util.Observable
16653  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16654  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16655  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16656  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16657  * All visual components (widgets) that require rendering into a layout should subclass Component.
16658  * @constructor
16659  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16660  * 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
16661  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16662  */
16663 Roo.Component = function(config){
16664     config = config || {};
16665     if(config.tagName || config.dom || typeof config == "string"){ // element object
16666         config = {el: config, id: config.id || config};
16667     }
16668     this.initialConfig = config;
16669
16670     Roo.apply(this, config);
16671     this.addEvents({
16672         /**
16673          * @event disable
16674          * Fires after the component is disabled.
16675              * @param {Roo.Component} this
16676              */
16677         disable : true,
16678         /**
16679          * @event enable
16680          * Fires after the component is enabled.
16681              * @param {Roo.Component} this
16682              */
16683         enable : true,
16684         /**
16685          * @event beforeshow
16686          * Fires before the component is shown.  Return false to stop the show.
16687              * @param {Roo.Component} this
16688              */
16689         beforeshow : true,
16690         /**
16691          * @event show
16692          * Fires after the component is shown.
16693              * @param {Roo.Component} this
16694              */
16695         show : true,
16696         /**
16697          * @event beforehide
16698          * Fires before the component is hidden. Return false to stop the hide.
16699              * @param {Roo.Component} this
16700              */
16701         beforehide : true,
16702         /**
16703          * @event hide
16704          * Fires after the component is hidden.
16705              * @param {Roo.Component} this
16706              */
16707         hide : true,
16708         /**
16709          * @event beforerender
16710          * Fires before the component is rendered. Return false to stop the render.
16711              * @param {Roo.Component} this
16712              */
16713         beforerender : true,
16714         /**
16715          * @event render
16716          * Fires after the component is rendered.
16717              * @param {Roo.Component} this
16718              */
16719         render : true,
16720         /**
16721          * @event beforedestroy
16722          * Fires before the component is destroyed. Return false to stop the destroy.
16723              * @param {Roo.Component} this
16724              */
16725         beforedestroy : true,
16726         /**
16727          * @event destroy
16728          * Fires after the component is destroyed.
16729              * @param {Roo.Component} this
16730              */
16731         destroy : true
16732     });
16733     if(!this.id){
16734         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16735     }
16736     Roo.ComponentMgr.register(this);
16737     Roo.Component.superclass.constructor.call(this);
16738     this.initComponent();
16739     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16740         this.render(this.renderTo);
16741         delete this.renderTo;
16742     }
16743 };
16744
16745 /** @private */
16746 Roo.Component.AUTO_ID = 1000;
16747
16748 Roo.extend(Roo.Component, Roo.util.Observable, {
16749     /**
16750      * @scope Roo.Component.prototype
16751      * @type {Boolean}
16752      * true if this component is hidden. Read-only.
16753      */
16754     hidden : false,
16755     /**
16756      * @type {Boolean}
16757      * true if this component is disabled. Read-only.
16758      */
16759     disabled : false,
16760     /**
16761      * @type {Boolean}
16762      * true if this component has been rendered. Read-only.
16763      */
16764     rendered : false,
16765     
16766     /** @cfg {String} disableClass
16767      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16768      */
16769     disabledClass : "x-item-disabled",
16770         /** @cfg {Boolean} allowDomMove
16771          * Whether the component can move the Dom node when rendering (defaults to true).
16772          */
16773     allowDomMove : true,
16774     /** @cfg {String} hideMode (display|visibility)
16775      * How this component should hidden. Supported values are
16776      * "visibility" (css visibility), "offsets" (negative offset position) and
16777      * "display" (css display) - defaults to "display".
16778      */
16779     hideMode: 'display',
16780
16781     /** @private */
16782     ctype : "Roo.Component",
16783
16784     /**
16785      * @cfg {String} actionMode 
16786      * which property holds the element that used for  hide() / show() / disable() / enable()
16787      * default is 'el' for forms you probably want to set this to fieldEl 
16788      */
16789     actionMode : "el",
16790
16791     /** @private */
16792     getActionEl : function(){
16793         return this[this.actionMode];
16794     },
16795
16796     initComponent : Roo.emptyFn,
16797     /**
16798      * If this is a lazy rendering component, render it to its container element.
16799      * @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.
16800      */
16801     render : function(container, position){
16802         
16803         if(this.rendered){
16804             return this;
16805         }
16806         
16807         if(this.fireEvent("beforerender", this) === false){
16808             return false;
16809         }
16810         
16811         if(!container && this.el){
16812             this.el = Roo.get(this.el);
16813             container = this.el.dom.parentNode;
16814             this.allowDomMove = false;
16815         }
16816         this.container = Roo.get(container);
16817         this.rendered = true;
16818         if(position !== undefined){
16819             if(typeof position == 'number'){
16820                 position = this.container.dom.childNodes[position];
16821             }else{
16822                 position = Roo.getDom(position);
16823             }
16824         }
16825         this.onRender(this.container, position || null);
16826         if(this.cls){
16827             this.el.addClass(this.cls);
16828             delete this.cls;
16829         }
16830         if(this.style){
16831             this.el.applyStyles(this.style);
16832             delete this.style;
16833         }
16834         this.fireEvent("render", this);
16835         this.afterRender(this.container);
16836         if(this.hidden){
16837             this.hide();
16838         }
16839         if(this.disabled){
16840             this.disable();
16841         }
16842
16843         return this;
16844         
16845     },
16846
16847     /** @private */
16848     // default function is not really useful
16849     onRender : function(ct, position){
16850         if(this.el){
16851             this.el = Roo.get(this.el);
16852             if(this.allowDomMove !== false){
16853                 ct.dom.insertBefore(this.el.dom, position);
16854             }
16855         }
16856     },
16857
16858     /** @private */
16859     getAutoCreate : function(){
16860         var cfg = typeof this.autoCreate == "object" ?
16861                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16862         if(this.id && !cfg.id){
16863             cfg.id = this.id;
16864         }
16865         return cfg;
16866     },
16867
16868     /** @private */
16869     afterRender : Roo.emptyFn,
16870
16871     /**
16872      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16873      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16874      */
16875     destroy : function(){
16876         if(this.fireEvent("beforedestroy", this) !== false){
16877             this.purgeListeners();
16878             this.beforeDestroy();
16879             if(this.rendered){
16880                 this.el.removeAllListeners();
16881                 this.el.remove();
16882                 if(this.actionMode == "container"){
16883                     this.container.remove();
16884                 }
16885             }
16886             this.onDestroy();
16887             Roo.ComponentMgr.unregister(this);
16888             this.fireEvent("destroy", this);
16889         }
16890     },
16891
16892         /** @private */
16893     beforeDestroy : function(){
16894
16895     },
16896
16897         /** @private */
16898         onDestroy : function(){
16899
16900     },
16901
16902     /**
16903      * Returns the underlying {@link Roo.Element}.
16904      * @return {Roo.Element} The element
16905      */
16906     getEl : function(){
16907         return this.el;
16908     },
16909
16910     /**
16911      * Returns the id of this component.
16912      * @return {String}
16913      */
16914     getId : function(){
16915         return this.id;
16916     },
16917
16918     /**
16919      * Try to focus this component.
16920      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16921      * @return {Roo.Component} this
16922      */
16923     focus : function(selectText){
16924         if(this.rendered){
16925             this.el.focus();
16926             if(selectText === true){
16927                 this.el.dom.select();
16928             }
16929         }
16930         return this;
16931     },
16932
16933     /** @private */
16934     blur : function(){
16935         if(this.rendered){
16936             this.el.blur();
16937         }
16938         return this;
16939     },
16940
16941     /**
16942      * Disable this component.
16943      * @return {Roo.Component} this
16944      */
16945     disable : function(){
16946         if(this.rendered){
16947             this.onDisable();
16948         }
16949         this.disabled = true;
16950         this.fireEvent("disable", this);
16951         return this;
16952     },
16953
16954         // private
16955     onDisable : function(){
16956         this.getActionEl().addClass(this.disabledClass);
16957         this.el.dom.disabled = true;
16958     },
16959
16960     /**
16961      * Enable this component.
16962      * @return {Roo.Component} this
16963      */
16964     enable : function(){
16965         if(this.rendered){
16966             this.onEnable();
16967         }
16968         this.disabled = false;
16969         this.fireEvent("enable", this);
16970         return this;
16971     },
16972
16973         // private
16974     onEnable : function(){
16975         this.getActionEl().removeClass(this.disabledClass);
16976         this.el.dom.disabled = false;
16977     },
16978
16979     /**
16980      * Convenience function for setting disabled/enabled by boolean.
16981      * @param {Boolean} disabled
16982      */
16983     setDisabled : function(disabled){
16984         this[disabled ? "disable" : "enable"]();
16985     },
16986
16987     /**
16988      * Show this component.
16989      * @return {Roo.Component} this
16990      */
16991     show: function(){
16992         if(this.fireEvent("beforeshow", this) !== false){
16993             this.hidden = false;
16994             if(this.rendered){
16995                 this.onShow();
16996             }
16997             this.fireEvent("show", this);
16998         }
16999         return this;
17000     },
17001
17002     // private
17003     onShow : function(){
17004         var ae = this.getActionEl();
17005         if(this.hideMode == 'visibility'){
17006             ae.dom.style.visibility = "visible";
17007         }else if(this.hideMode == 'offsets'){
17008             ae.removeClass('x-hidden');
17009         }else{
17010             ae.dom.style.display = "";
17011         }
17012     },
17013
17014     /**
17015      * Hide this component.
17016      * @return {Roo.Component} this
17017      */
17018     hide: function(){
17019         if(this.fireEvent("beforehide", this) !== false){
17020             this.hidden = true;
17021             if(this.rendered){
17022                 this.onHide();
17023             }
17024             this.fireEvent("hide", this);
17025         }
17026         return this;
17027     },
17028
17029     // private
17030     onHide : function(){
17031         var ae = this.getActionEl();
17032         if(this.hideMode == 'visibility'){
17033             ae.dom.style.visibility = "hidden";
17034         }else if(this.hideMode == 'offsets'){
17035             ae.addClass('x-hidden');
17036         }else{
17037             ae.dom.style.display = "none";
17038         }
17039     },
17040
17041     /**
17042      * Convenience function to hide or show this component by boolean.
17043      * @param {Boolean} visible True to show, false to hide
17044      * @return {Roo.Component} this
17045      */
17046     setVisible: function(visible){
17047         if(visible) {
17048             this.show();
17049         }else{
17050             this.hide();
17051         }
17052         return this;
17053     },
17054
17055     /**
17056      * Returns true if this component is visible.
17057      */
17058     isVisible : function(){
17059         return this.getActionEl().isVisible();
17060     },
17061
17062     cloneConfig : function(overrides){
17063         overrides = overrides || {};
17064         var id = overrides.id || Roo.id();
17065         var cfg = Roo.applyIf(overrides, this.initialConfig);
17066         cfg.id = id; // prevent dup id
17067         return new this.constructor(cfg);
17068     }
17069 });/*
17070  * Based on:
17071  * Ext JS Library 1.1.1
17072  * Copyright(c) 2006-2007, Ext JS, LLC.
17073  *
17074  * Originally Released Under LGPL - original licence link has changed is not relivant.
17075  *
17076  * Fork - LGPL
17077  * <script type="text/javascript">
17078  */
17079
17080 /**
17081  * @class Roo.BoxComponent
17082  * @extends Roo.Component
17083  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
17084  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
17085  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
17086  * layout containers.
17087  * @constructor
17088  * @param {Roo.Element/String/Object} config The configuration options.
17089  */
17090 Roo.BoxComponent = function(config){
17091     Roo.Component.call(this, config);
17092     this.addEvents({
17093         /**
17094          * @event resize
17095          * Fires after the component is resized.
17096              * @param {Roo.Component} this
17097              * @param {Number} adjWidth The box-adjusted width that was set
17098              * @param {Number} adjHeight The box-adjusted height that was set
17099              * @param {Number} rawWidth The width that was originally specified
17100              * @param {Number} rawHeight The height that was originally specified
17101              */
17102         resize : true,
17103         /**
17104          * @event move
17105          * Fires after the component is moved.
17106              * @param {Roo.Component} this
17107              * @param {Number} x The new x position
17108              * @param {Number} y The new y position
17109              */
17110         move : true
17111     });
17112 };
17113
17114 Roo.extend(Roo.BoxComponent, Roo.Component, {
17115     // private, set in afterRender to signify that the component has been rendered
17116     boxReady : false,
17117     // private, used to defer height settings to subclasses
17118     deferHeight: false,
17119     /** @cfg {Number} width
17120      * width (optional) size of component
17121      */
17122      /** @cfg {Number} height
17123      * height (optional) size of component
17124      */
17125      
17126     /**
17127      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
17128      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
17129      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
17130      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
17131      * @return {Roo.BoxComponent} this
17132      */
17133     setSize : function(w, h){
17134         // support for standard size objects
17135         if(typeof w == 'object'){
17136             h = w.height;
17137             w = w.width;
17138         }
17139         // not rendered
17140         if(!this.boxReady){
17141             this.width = w;
17142             this.height = h;
17143             return this;
17144         }
17145
17146         // prevent recalcs when not needed
17147         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
17148             return this;
17149         }
17150         this.lastSize = {width: w, height: h};
17151
17152         var adj = this.adjustSize(w, h);
17153         var aw = adj.width, ah = adj.height;
17154         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
17155             var rz = this.getResizeEl();
17156             if(!this.deferHeight && aw !== undefined && ah !== undefined){
17157                 rz.setSize(aw, ah);
17158             }else if(!this.deferHeight && ah !== undefined){
17159                 rz.setHeight(ah);
17160             }else if(aw !== undefined){
17161                 rz.setWidth(aw);
17162             }
17163             this.onResize(aw, ah, w, h);
17164             this.fireEvent('resize', this, aw, ah, w, h);
17165         }
17166         return this;
17167     },
17168
17169     /**
17170      * Gets the current size of the component's underlying element.
17171      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
17172      */
17173     getSize : function(){
17174         return this.el.getSize();
17175     },
17176
17177     /**
17178      * Gets the current XY position of the component's underlying element.
17179      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17180      * @return {Array} The XY position of the element (e.g., [100, 200])
17181      */
17182     getPosition : function(local){
17183         if(local === true){
17184             return [this.el.getLeft(true), this.el.getTop(true)];
17185         }
17186         return this.xy || this.el.getXY();
17187     },
17188
17189     /**
17190      * Gets the current box measurements of the component's underlying element.
17191      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
17192      * @returns {Object} box An object in the format {x, y, width, height}
17193      */
17194     getBox : function(local){
17195         var s = this.el.getSize();
17196         if(local){
17197             s.x = this.el.getLeft(true);
17198             s.y = this.el.getTop(true);
17199         }else{
17200             var xy = this.xy || this.el.getXY();
17201             s.x = xy[0];
17202             s.y = xy[1];
17203         }
17204         return s;
17205     },
17206
17207     /**
17208      * Sets the current box measurements of the component's underlying element.
17209      * @param {Object} box An object in the format {x, y, width, height}
17210      * @returns {Roo.BoxComponent} this
17211      */
17212     updateBox : function(box){
17213         this.setSize(box.width, box.height);
17214         this.setPagePosition(box.x, box.y);
17215         return this;
17216     },
17217
17218     // protected
17219     getResizeEl : function(){
17220         return this.resizeEl || this.el;
17221     },
17222
17223     // protected
17224     getPositionEl : function(){
17225         return this.positionEl || this.el;
17226     },
17227
17228     /**
17229      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
17230      * This method fires the move event.
17231      * @param {Number} left The new left
17232      * @param {Number} top The new top
17233      * @returns {Roo.BoxComponent} this
17234      */
17235     setPosition : function(x, y){
17236         this.x = x;
17237         this.y = y;
17238         if(!this.boxReady){
17239             return this;
17240         }
17241         var adj = this.adjustPosition(x, y);
17242         var ax = adj.x, ay = adj.y;
17243
17244         var el = this.getPositionEl();
17245         if(ax !== undefined || ay !== undefined){
17246             if(ax !== undefined && ay !== undefined){
17247                 el.setLeftTop(ax, ay);
17248             }else if(ax !== undefined){
17249                 el.setLeft(ax);
17250             }else if(ay !== undefined){
17251                 el.setTop(ay);
17252             }
17253             this.onPosition(ax, ay);
17254             this.fireEvent('move', this, ax, ay);
17255         }
17256         return this;
17257     },
17258
17259     /**
17260      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
17261      * This method fires the move event.
17262      * @param {Number} x The new x position
17263      * @param {Number} y The new y position
17264      * @returns {Roo.BoxComponent} this
17265      */
17266     setPagePosition : function(x, y){
17267         this.pageX = x;
17268         this.pageY = y;
17269         if(!this.boxReady){
17270             return;
17271         }
17272         if(x === undefined || y === undefined){ // cannot translate undefined points
17273             return;
17274         }
17275         var p = this.el.translatePoints(x, y);
17276         this.setPosition(p.left, p.top);
17277         return this;
17278     },
17279
17280     // private
17281     onRender : function(ct, position){
17282         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
17283         if(this.resizeEl){
17284             this.resizeEl = Roo.get(this.resizeEl);
17285         }
17286         if(this.positionEl){
17287             this.positionEl = Roo.get(this.positionEl);
17288         }
17289     },
17290
17291     // private
17292     afterRender : function(){
17293         Roo.BoxComponent.superclass.afterRender.call(this);
17294         this.boxReady = true;
17295         this.setSize(this.width, this.height);
17296         if(this.x || this.y){
17297             this.setPosition(this.x, this.y);
17298         }
17299         if(this.pageX || this.pageY){
17300             this.setPagePosition(this.pageX, this.pageY);
17301         }
17302     },
17303
17304     /**
17305      * Force the component's size to recalculate based on the underlying element's current height and width.
17306      * @returns {Roo.BoxComponent} this
17307      */
17308     syncSize : function(){
17309         delete this.lastSize;
17310         this.setSize(this.el.getWidth(), this.el.getHeight());
17311         return this;
17312     },
17313
17314     /**
17315      * Called after the component is resized, this method is empty by default but can be implemented by any
17316      * subclass that needs to perform custom logic after a resize occurs.
17317      * @param {Number} adjWidth The box-adjusted width that was set
17318      * @param {Number} adjHeight The box-adjusted height that was set
17319      * @param {Number} rawWidth The width that was originally specified
17320      * @param {Number} rawHeight The height that was originally specified
17321      */
17322     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17323
17324     },
17325
17326     /**
17327      * Called after the component is moved, this method is empty by default but can be implemented by any
17328      * subclass that needs to perform custom logic after a move occurs.
17329      * @param {Number} x The new x position
17330      * @param {Number} y The new y position
17331      */
17332     onPosition : function(x, y){
17333
17334     },
17335
17336     // private
17337     adjustSize : function(w, h){
17338         if(this.autoWidth){
17339             w = 'auto';
17340         }
17341         if(this.autoHeight){
17342             h = 'auto';
17343         }
17344         return {width : w, height: h};
17345     },
17346
17347     // private
17348     adjustPosition : function(x, y){
17349         return {x : x, y: y};
17350     }
17351 });/*
17352  * Based on:
17353  * Ext JS Library 1.1.1
17354  * Copyright(c) 2006-2007, Ext JS, LLC.
17355  *
17356  * Originally Released Under LGPL - original licence link has changed is not relivant.
17357  *
17358  * Fork - LGPL
17359  * <script type="text/javascript">
17360  */
17361  (function(){ 
17362 /**
17363  * @class Roo.Layer
17364  * @extends Roo.Element
17365  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17366  * automatic maintaining of shadow/shim positions.
17367  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17368  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17369  * you can pass a string with a CSS class name. False turns off the shadow.
17370  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17371  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17372  * @cfg {String} cls CSS class to add to the element
17373  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17374  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17375  * @constructor
17376  * @param {Object} config An object with config options.
17377  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17378  */
17379
17380 Roo.Layer = function(config, existingEl){
17381     config = config || {};
17382     var dh = Roo.DomHelper;
17383     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17384     if(existingEl){
17385         this.dom = Roo.getDom(existingEl);
17386     }
17387     if(!this.dom){
17388         var o = config.dh || {tag: "div", cls: "x-layer"};
17389         this.dom = dh.append(pel, o);
17390     }
17391     if(config.cls){
17392         this.addClass(config.cls);
17393     }
17394     this.constrain = config.constrain !== false;
17395     this.visibilityMode = Roo.Element.VISIBILITY;
17396     if(config.id){
17397         this.id = this.dom.id = config.id;
17398     }else{
17399         this.id = Roo.id(this.dom);
17400     }
17401     this.zindex = config.zindex || this.getZIndex();
17402     this.position("absolute", this.zindex);
17403     if(config.shadow){
17404         this.shadowOffset = config.shadowOffset || 4;
17405         this.shadow = new Roo.Shadow({
17406             offset : this.shadowOffset,
17407             mode : config.shadow
17408         });
17409     }else{
17410         this.shadowOffset = 0;
17411     }
17412     this.useShim = config.shim !== false && Roo.useShims;
17413     this.useDisplay = config.useDisplay;
17414     this.hide();
17415 };
17416
17417 var supr = Roo.Element.prototype;
17418
17419 // shims are shared among layer to keep from having 100 iframes
17420 var shims = [];
17421
17422 Roo.extend(Roo.Layer, Roo.Element, {
17423
17424     getZIndex : function(){
17425         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17426     },
17427
17428     getShim : function(){
17429         if(!this.useShim){
17430             return null;
17431         }
17432         if(this.shim){
17433             return this.shim;
17434         }
17435         var shim = shims.shift();
17436         if(!shim){
17437             shim = this.createShim();
17438             shim.enableDisplayMode('block');
17439             shim.dom.style.display = 'none';
17440             shim.dom.style.visibility = 'visible';
17441         }
17442         var pn = this.dom.parentNode;
17443         if(shim.dom.parentNode != pn){
17444             pn.insertBefore(shim.dom, this.dom);
17445         }
17446         shim.setStyle('z-index', this.getZIndex()-2);
17447         this.shim = shim;
17448         return shim;
17449     },
17450
17451     hideShim : function(){
17452         if(this.shim){
17453             this.shim.setDisplayed(false);
17454             shims.push(this.shim);
17455             delete this.shim;
17456         }
17457     },
17458
17459     disableShadow : function(){
17460         if(this.shadow){
17461             this.shadowDisabled = true;
17462             this.shadow.hide();
17463             this.lastShadowOffset = this.shadowOffset;
17464             this.shadowOffset = 0;
17465         }
17466     },
17467
17468     enableShadow : function(show){
17469         if(this.shadow){
17470             this.shadowDisabled = false;
17471             this.shadowOffset = this.lastShadowOffset;
17472             delete this.lastShadowOffset;
17473             if(show){
17474                 this.sync(true);
17475             }
17476         }
17477     },
17478
17479     // private
17480     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17481     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17482     sync : function(doShow){
17483         var sw = this.shadow;
17484         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17485             var sh = this.getShim();
17486
17487             var w = this.getWidth(),
17488                 h = this.getHeight();
17489
17490             var l = this.getLeft(true),
17491                 t = this.getTop(true);
17492
17493             if(sw && !this.shadowDisabled){
17494                 if(doShow && !sw.isVisible()){
17495                     sw.show(this);
17496                 }else{
17497                     sw.realign(l, t, w, h);
17498                 }
17499                 if(sh){
17500                     if(doShow){
17501                        sh.show();
17502                     }
17503                     // fit the shim behind the shadow, so it is shimmed too
17504                     var a = sw.adjusts, s = sh.dom.style;
17505                     s.left = (Math.min(l, l+a.l))+"px";
17506                     s.top = (Math.min(t, t+a.t))+"px";
17507                     s.width = (w+a.w)+"px";
17508                     s.height = (h+a.h)+"px";
17509                 }
17510             }else if(sh){
17511                 if(doShow){
17512                    sh.show();
17513                 }
17514                 sh.setSize(w, h);
17515                 sh.setLeftTop(l, t);
17516             }
17517             
17518         }
17519     },
17520
17521     // private
17522     destroy : function(){
17523         this.hideShim();
17524         if(this.shadow){
17525             this.shadow.hide();
17526         }
17527         this.removeAllListeners();
17528         var pn = this.dom.parentNode;
17529         if(pn){
17530             pn.removeChild(this.dom);
17531         }
17532         Roo.Element.uncache(this.id);
17533     },
17534
17535     remove : function(){
17536         this.destroy();
17537     },
17538
17539     // private
17540     beginUpdate : function(){
17541         this.updating = true;
17542     },
17543
17544     // private
17545     endUpdate : function(){
17546         this.updating = false;
17547         this.sync(true);
17548     },
17549
17550     // private
17551     hideUnders : function(negOffset){
17552         if(this.shadow){
17553             this.shadow.hide();
17554         }
17555         this.hideShim();
17556     },
17557
17558     // private
17559     constrainXY : function(){
17560         if(this.constrain){
17561             var vw = Roo.lib.Dom.getViewWidth(),
17562                 vh = Roo.lib.Dom.getViewHeight();
17563             var s = Roo.get(document).getScroll();
17564
17565             var xy = this.getXY();
17566             var x = xy[0], y = xy[1];   
17567             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17568             // only move it if it needs it
17569             var moved = false;
17570             // first validate right/bottom
17571             if((x + w) > vw+s.left){
17572                 x = vw - w - this.shadowOffset;
17573                 moved = true;
17574             }
17575             if((y + h) > vh+s.top){
17576                 y = vh - h - this.shadowOffset;
17577                 moved = true;
17578             }
17579             // then make sure top/left isn't negative
17580             if(x < s.left){
17581                 x = s.left;
17582                 moved = true;
17583             }
17584             if(y < s.top){
17585                 y = s.top;
17586                 moved = true;
17587             }
17588             if(moved){
17589                 if(this.avoidY){
17590                     var ay = this.avoidY;
17591                     if(y <= ay && (y+h) >= ay){
17592                         y = ay-h-5;   
17593                     }
17594                 }
17595                 xy = [x, y];
17596                 this.storeXY(xy);
17597                 supr.setXY.call(this, xy);
17598                 this.sync();
17599             }
17600         }
17601     },
17602
17603     isVisible : function(){
17604         return this.visible;    
17605     },
17606
17607     // private
17608     showAction : function(){
17609         this.visible = true; // track visibility to prevent getStyle calls
17610         if(this.useDisplay === true){
17611             this.setDisplayed("");
17612         }else if(this.lastXY){
17613             supr.setXY.call(this, this.lastXY);
17614         }else if(this.lastLT){
17615             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17616         }
17617     },
17618
17619     // private
17620     hideAction : function(){
17621         this.visible = false;
17622         if(this.useDisplay === true){
17623             this.setDisplayed(false);
17624         }else{
17625             this.setLeftTop(-10000,-10000);
17626         }
17627     },
17628
17629     // overridden Element method
17630     setVisible : function(v, a, d, c, e){
17631         if(v){
17632             this.showAction();
17633         }
17634         if(a && v){
17635             var cb = function(){
17636                 this.sync(true);
17637                 if(c){
17638                     c();
17639                 }
17640             }.createDelegate(this);
17641             supr.setVisible.call(this, true, true, d, cb, e);
17642         }else{
17643             if(!v){
17644                 this.hideUnders(true);
17645             }
17646             var cb = c;
17647             if(a){
17648                 cb = function(){
17649                     this.hideAction();
17650                     if(c){
17651                         c();
17652                     }
17653                 }.createDelegate(this);
17654             }
17655             supr.setVisible.call(this, v, a, d, cb, e);
17656             if(v){
17657                 this.sync(true);
17658             }else if(!a){
17659                 this.hideAction();
17660             }
17661         }
17662     },
17663
17664     storeXY : function(xy){
17665         delete this.lastLT;
17666         this.lastXY = xy;
17667     },
17668
17669     storeLeftTop : function(left, top){
17670         delete this.lastXY;
17671         this.lastLT = [left, top];
17672     },
17673
17674     // private
17675     beforeFx : function(){
17676         this.beforeAction();
17677         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17678     },
17679
17680     // private
17681     afterFx : function(){
17682         Roo.Layer.superclass.afterFx.apply(this, arguments);
17683         this.sync(this.isVisible());
17684     },
17685
17686     // private
17687     beforeAction : function(){
17688         if(!this.updating && this.shadow){
17689             this.shadow.hide();
17690         }
17691     },
17692
17693     // overridden Element method
17694     setLeft : function(left){
17695         this.storeLeftTop(left, this.getTop(true));
17696         supr.setLeft.apply(this, arguments);
17697         this.sync();
17698     },
17699
17700     setTop : function(top){
17701         this.storeLeftTop(this.getLeft(true), top);
17702         supr.setTop.apply(this, arguments);
17703         this.sync();
17704     },
17705
17706     setLeftTop : function(left, top){
17707         this.storeLeftTop(left, top);
17708         supr.setLeftTop.apply(this, arguments);
17709         this.sync();
17710     },
17711
17712     setXY : function(xy, a, d, c, e){
17713         this.fixDisplay();
17714         this.beforeAction();
17715         this.storeXY(xy);
17716         var cb = this.createCB(c);
17717         supr.setXY.call(this, xy, a, d, cb, e);
17718         if(!a){
17719             cb();
17720         }
17721     },
17722
17723     // private
17724     createCB : function(c){
17725         var el = this;
17726         return function(){
17727             el.constrainXY();
17728             el.sync(true);
17729             if(c){
17730                 c();
17731             }
17732         };
17733     },
17734
17735     // overridden Element method
17736     setX : function(x, a, d, c, e){
17737         this.setXY([x, this.getY()], a, d, c, e);
17738     },
17739
17740     // overridden Element method
17741     setY : function(y, a, d, c, e){
17742         this.setXY([this.getX(), y], a, d, c, e);
17743     },
17744
17745     // overridden Element method
17746     setSize : function(w, h, a, d, c, e){
17747         this.beforeAction();
17748         var cb = this.createCB(c);
17749         supr.setSize.call(this, w, h, a, d, cb, e);
17750         if(!a){
17751             cb();
17752         }
17753     },
17754
17755     // overridden Element method
17756     setWidth : function(w, a, d, c, e){
17757         this.beforeAction();
17758         var cb = this.createCB(c);
17759         supr.setWidth.call(this, w, a, d, cb, e);
17760         if(!a){
17761             cb();
17762         }
17763     },
17764
17765     // overridden Element method
17766     setHeight : function(h, a, d, c, e){
17767         this.beforeAction();
17768         var cb = this.createCB(c);
17769         supr.setHeight.call(this, h, a, d, cb, e);
17770         if(!a){
17771             cb();
17772         }
17773     },
17774
17775     // overridden Element method
17776     setBounds : function(x, y, w, h, a, d, c, e){
17777         this.beforeAction();
17778         var cb = this.createCB(c);
17779         if(!a){
17780             this.storeXY([x, y]);
17781             supr.setXY.call(this, [x, y]);
17782             supr.setSize.call(this, w, h, a, d, cb, e);
17783             cb();
17784         }else{
17785             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17786         }
17787         return this;
17788     },
17789     
17790     /**
17791      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17792      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17793      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17794      * @param {Number} zindex The new z-index to set
17795      * @return {this} The Layer
17796      */
17797     setZIndex : function(zindex){
17798         this.zindex = zindex;
17799         this.setStyle("z-index", zindex + 2);
17800         if(this.shadow){
17801             this.shadow.setZIndex(zindex + 1);
17802         }
17803         if(this.shim){
17804             this.shim.setStyle("z-index", zindex);
17805         }
17806     }
17807 });
17808 })();/*
17809  * Original code for Roojs - LGPL
17810  * <script type="text/javascript">
17811  */
17812  
17813 /**
17814  * @class Roo.XComponent
17815  * A delayed Element creator...
17816  * Or a way to group chunks of interface together.
17817  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17818  *  used in conjunction with XComponent.build() it will create an instance of each element,
17819  *  then call addxtype() to build the User interface.
17820  * 
17821  * Mypart.xyx = new Roo.XComponent({
17822
17823     parent : 'Mypart.xyz', // empty == document.element.!!
17824     order : '001',
17825     name : 'xxxx'
17826     region : 'xxxx'
17827     disabled : function() {} 
17828      
17829     tree : function() { // return an tree of xtype declared components
17830         var MODULE = this;
17831         return 
17832         {
17833             xtype : 'NestedLayoutPanel',
17834             // technicall
17835         }
17836      ]
17837  *})
17838  *
17839  *
17840  * It can be used to build a big heiracy, with parent etc.
17841  * or you can just use this to render a single compoent to a dom element
17842  * MYPART.render(Roo.Element | String(id) | dom_element )
17843  *
17844  *
17845  * Usage patterns.
17846  *
17847  * Classic Roo
17848  *
17849  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17850  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17851  *
17852  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17853  *
17854  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17855  * - if mulitple topModules exist, the last one is defined as the top module.
17856  *
17857  * Embeded Roo
17858  * 
17859  * When the top level or multiple modules are to embedded into a existing HTML page,
17860  * the parent element can container '#id' of the element where the module will be drawn.
17861  *
17862  * Bootstrap Roo
17863  *
17864  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17865  * it relies more on a include mechanism, where sub modules are included into an outer page.
17866  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17867  * 
17868  * Bootstrap Roo Included elements
17869  *
17870  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17871  * hence confusing the component builder as it thinks there are multiple top level elements. 
17872  *
17873  * String Over-ride & Translations
17874  *
17875  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17876  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17877  * are needed. @see Roo.XComponent.overlayString  
17878  * 
17879  * 
17880  * 
17881  * @extends Roo.util.Observable
17882  * @constructor
17883  * @param cfg {Object} configuration of component
17884  * 
17885  */
17886 Roo.XComponent = function(cfg) {
17887     Roo.apply(this, cfg);
17888     this.addEvents({ 
17889         /**
17890              * @event built
17891              * Fires when this the componnt is built
17892              * @param {Roo.XComponent} c the component
17893              */
17894         'built' : true
17895         
17896     });
17897     this.region = this.region || 'center'; // default..
17898     Roo.XComponent.register(this);
17899     this.modules = false;
17900     this.el = false; // where the layout goes..
17901     
17902     
17903 }
17904 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17905     /**
17906      * @property el
17907      * The created element (with Roo.factory())
17908      * @type {Roo.Layout}
17909      */
17910     el  : false,
17911     
17912     /**
17913      * @property el
17914      * for BC  - use el in new code
17915      * @type {Roo.Layout}
17916      */
17917     panel : false,
17918     
17919     /**
17920      * @property layout
17921      * for BC  - use el in new code
17922      * @type {Roo.Layout}
17923      */
17924     layout : false,
17925     
17926      /**
17927      * @cfg {Function|boolean} disabled
17928      * If this module is disabled by some rule, return true from the funtion
17929      */
17930     disabled : false,
17931     
17932     /**
17933      * @cfg {String} parent 
17934      * Name of parent element which it get xtype added to..
17935      */
17936     parent: false,
17937     
17938     /**
17939      * @cfg {String} order
17940      * Used to set the order in which elements are created (usefull for multiple tabs)
17941      */
17942     
17943     order : false,
17944     /**
17945      * @cfg {String} name
17946      * String to display while loading.
17947      */
17948     name : false,
17949     /**
17950      * @cfg {String} region
17951      * Region to render component to (defaults to center)
17952      */
17953     region : 'center',
17954     
17955     /**
17956      * @cfg {Array} items
17957      * A single item array - the first element is the root of the tree..
17958      * It's done this way to stay compatible with the Xtype system...
17959      */
17960     items : false,
17961     
17962     /**
17963      * @property _tree
17964      * The method that retuns the tree of parts that make up this compoennt 
17965      * @type {function}
17966      */
17967     _tree  : false,
17968     
17969      /**
17970      * render
17971      * render element to dom or tree
17972      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17973      */
17974     
17975     render : function(el)
17976     {
17977         
17978         el = el || false;
17979         var hp = this.parent ? 1 : 0;
17980         Roo.debug &&  Roo.log(this);
17981         
17982         var tree = this._tree ? this._tree() : this.tree();
17983
17984         
17985         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17986             // if parent is a '#.....' string, then let's use that..
17987             var ename = this.parent.substr(1);
17988             this.parent = false;
17989             Roo.debug && Roo.log(ename);
17990             switch (ename) {
17991                 case 'bootstrap-body':
17992                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17993                         // this is the BorderLayout standard?
17994                        this.parent = { el : true };
17995                        break;
17996                     }
17997                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17998                         // need to insert stuff...
17999                         this.parent =  {
18000                              el : new Roo.bootstrap.layout.Border({
18001                                  el : document.body, 
18002                      
18003                                  center: {
18004                                     titlebar: false,
18005                                     autoScroll:false,
18006                                     closeOnTab: true,
18007                                     tabPosition: 'top',
18008                                       //resizeTabs: true,
18009                                     alwaysShowTabs: true,
18010                                     hideTabs: false
18011                                      //minTabWidth: 140
18012                                  }
18013                              })
18014                         
18015                          };
18016                          break;
18017                     }
18018                          
18019                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
18020                         this.parent = { el :  new  Roo.bootstrap.Body() };
18021                         Roo.debug && Roo.log("setting el to doc body");
18022                          
18023                     } else {
18024                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
18025                     }
18026                     break;
18027                 case 'bootstrap':
18028                     this.parent = { el : true};
18029                     // fall through
18030                 default:
18031                     el = Roo.get(ename);
18032                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
18033                         this.parent = { el : true};
18034                     }
18035                     
18036                     break;
18037             }
18038                 
18039             
18040             if (!el && !this.parent) {
18041                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
18042                 return;
18043             }
18044         }
18045         
18046         Roo.debug && Roo.log("EL:");
18047         Roo.debug && Roo.log(el);
18048         Roo.debug && Roo.log("this.parent.el:");
18049         Roo.debug && Roo.log(this.parent.el);
18050         
18051
18052         // altertive root elements ??? - we need a better way to indicate these.
18053         var is_alt = Roo.XComponent.is_alt ||
18054                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
18055                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
18056                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
18057         
18058         
18059         
18060         if (!this.parent && is_alt) {
18061             //el = Roo.get(document.body);
18062             this.parent = { el : true };
18063         }
18064             
18065             
18066         
18067         if (!this.parent) {
18068             
18069             Roo.debug && Roo.log("no parent - creating one");
18070             
18071             el = el ? Roo.get(el) : false;      
18072             
18073             if (typeof(Roo.BorderLayout) == 'undefined' ) {
18074                 
18075                 this.parent =  {
18076                     el : new Roo.bootstrap.layout.Border({
18077                         el: el || document.body,
18078                     
18079                         center: {
18080                             titlebar: false,
18081                             autoScroll:false,
18082                             closeOnTab: true,
18083                             tabPosition: 'top',
18084                              //resizeTabs: true,
18085                             alwaysShowTabs: false,
18086                             hideTabs: true,
18087                             minTabWidth: 140,
18088                             overflow: 'visible'
18089                          }
18090                      })
18091                 };
18092             } else {
18093             
18094                 // it's a top level one..
18095                 this.parent =  {
18096                     el : new Roo.BorderLayout(el || document.body, {
18097                         center: {
18098                             titlebar: false,
18099                             autoScroll:false,
18100                             closeOnTab: true,
18101                             tabPosition: 'top',
18102                              //resizeTabs: true,
18103                             alwaysShowTabs: el && hp? false :  true,
18104                             hideTabs: el || !hp ? true :  false,
18105                             minTabWidth: 140
18106                          }
18107                     })
18108                 };
18109             }
18110         }
18111         
18112         if (!this.parent.el) {
18113                 // probably an old style ctor, which has been disabled.
18114                 return;
18115
18116         }
18117                 // The 'tree' method is  '_tree now' 
18118             
18119         tree.region = tree.region || this.region;
18120         var is_body = false;
18121         if (this.parent.el === true) {
18122             // bootstrap... - body..
18123             if (el) {
18124                 tree.el = el;
18125             }
18126             this.parent.el = Roo.factory(tree);
18127             is_body = true;
18128         }
18129         
18130         this.el = this.parent.el.addxtype(tree, undefined, is_body);
18131         this.fireEvent('built', this);
18132         
18133         this.panel = this.el;
18134         this.layout = this.panel.layout;
18135         this.parentLayout = this.parent.layout  || false;  
18136          
18137     }
18138     
18139 });
18140
18141 Roo.apply(Roo.XComponent, {
18142     /**
18143      * @property  hideProgress
18144      * true to disable the building progress bar.. usefull on single page renders.
18145      * @type Boolean
18146      */
18147     hideProgress : false,
18148     /**
18149      * @property  buildCompleted
18150      * True when the builder has completed building the interface.
18151      * @type Boolean
18152      */
18153     buildCompleted : false,
18154      
18155     /**
18156      * @property  topModule
18157      * the upper most module - uses document.element as it's constructor.
18158      * @type Object
18159      */
18160      
18161     topModule  : false,
18162       
18163     /**
18164      * @property  modules
18165      * array of modules to be created by registration system.
18166      * @type {Array} of Roo.XComponent
18167      */
18168     
18169     modules : [],
18170     /**
18171      * @property  elmodules
18172      * array of modules to be created by which use #ID 
18173      * @type {Array} of Roo.XComponent
18174      */
18175      
18176     elmodules : [],
18177
18178      /**
18179      * @property  is_alt
18180      * Is an alternative Root - normally used by bootstrap or other systems,
18181      *    where the top element in the tree can wrap 'body' 
18182      * @type {boolean}  (default false)
18183      */
18184      
18185     is_alt : false,
18186     /**
18187      * @property  build_from_html
18188      * Build elements from html - used by bootstrap HTML stuff 
18189      *    - this is cleared after build is completed
18190      * @type {boolean}    (default false)
18191      */
18192      
18193     build_from_html : false,
18194     /**
18195      * Register components to be built later.
18196      *
18197      * This solves the following issues
18198      * - Building is not done on page load, but after an authentication process has occured.
18199      * - Interface elements are registered on page load
18200      * - Parent Interface elements may not be loaded before child, so this handles that..
18201      * 
18202      *
18203      * example:
18204      * 
18205      * MyApp.register({
18206           order : '000001',
18207           module : 'Pman.Tab.projectMgr',
18208           region : 'center',
18209           parent : 'Pman.layout',
18210           disabled : false,  // or use a function..
18211         })
18212      
18213      * * @param {Object} details about module
18214      */
18215     register : function(obj) {
18216                 
18217         Roo.XComponent.event.fireEvent('register', obj);
18218         switch(typeof(obj.disabled) ) {
18219                 
18220             case 'undefined':
18221                 break;
18222             
18223             case 'function':
18224                 if ( obj.disabled() ) {
18225                         return;
18226                 }
18227                 break;
18228             
18229             default:
18230                 if (obj.disabled || obj.region == '#disabled') {
18231                         return;
18232                 }
18233                 break;
18234         }
18235                 
18236         this.modules.push(obj);
18237          
18238     },
18239     /**
18240      * convert a string to an object..
18241      * eg. 'AAA.BBB' -> finds AAA.BBB
18242
18243      */
18244     
18245     toObject : function(str)
18246     {
18247         if (!str || typeof(str) == 'object') {
18248             return str;
18249         }
18250         if (str.substring(0,1) == '#') {
18251             return str;
18252         }
18253
18254         var ar = str.split('.');
18255         var rt, o;
18256         rt = ar.shift();
18257             /** eval:var:o */
18258         try {
18259             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
18260         } catch (e) {
18261             throw "Module not found : " + str;
18262         }
18263         
18264         if (o === false) {
18265             throw "Module not found : " + str;
18266         }
18267         Roo.each(ar, function(e) {
18268             if (typeof(o[e]) == 'undefined') {
18269                 throw "Module not found : " + str;
18270             }
18271             o = o[e];
18272         });
18273         
18274         return o;
18275         
18276     },
18277     
18278     
18279     /**
18280      * move modules into their correct place in the tree..
18281      * 
18282      */
18283     preBuild : function ()
18284     {
18285         var _t = this;
18286         Roo.each(this.modules , function (obj)
18287         {
18288             Roo.XComponent.event.fireEvent('beforebuild', obj);
18289             
18290             var opar = obj.parent;
18291             try { 
18292                 obj.parent = this.toObject(opar);
18293             } catch(e) {
18294                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
18295                 return;
18296             }
18297             
18298             if (!obj.parent) {
18299                 Roo.debug && Roo.log("GOT top level module");
18300                 Roo.debug && Roo.log(obj);
18301                 obj.modules = new Roo.util.MixedCollection(false, 
18302                     function(o) { return o.order + '' }
18303                 );
18304                 this.topModule = obj;
18305                 return;
18306             }
18307                         // parent is a string (usually a dom element name..)
18308             if (typeof(obj.parent) == 'string') {
18309                 this.elmodules.push(obj);
18310                 return;
18311             }
18312             if (obj.parent.constructor != Roo.XComponent) {
18313                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
18314             }
18315             if (!obj.parent.modules) {
18316                 obj.parent.modules = new Roo.util.MixedCollection(false, 
18317                     function(o) { return o.order + '' }
18318                 );
18319             }
18320             if (obj.parent.disabled) {
18321                 obj.disabled = true;
18322             }
18323             obj.parent.modules.add(obj);
18324         }, this);
18325     },
18326     
18327      /**
18328      * make a list of modules to build.
18329      * @return {Array} list of modules. 
18330      */ 
18331     
18332     buildOrder : function()
18333     {
18334         var _this = this;
18335         var cmp = function(a,b) {   
18336             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18337         };
18338         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18339             throw "No top level modules to build";
18340         }
18341         
18342         // make a flat list in order of modules to build.
18343         var mods = this.topModule ? [ this.topModule ] : [];
18344                 
18345         
18346         // elmodules (is a list of DOM based modules )
18347         Roo.each(this.elmodules, function(e) {
18348             mods.push(e);
18349             if (!this.topModule &&
18350                 typeof(e.parent) == 'string' &&
18351                 e.parent.substring(0,1) == '#' &&
18352                 Roo.get(e.parent.substr(1))
18353                ) {
18354                 
18355                 _this.topModule = e;
18356             }
18357             
18358         });
18359
18360         
18361         // add modules to their parents..
18362         var addMod = function(m) {
18363             Roo.debug && Roo.log("build Order: add: " + m.name);
18364                 
18365             mods.push(m);
18366             if (m.modules && !m.disabled) {
18367                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18368                 m.modules.keySort('ASC',  cmp );
18369                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18370     
18371                 m.modules.each(addMod);
18372             } else {
18373                 Roo.debug && Roo.log("build Order: no child modules");
18374             }
18375             // not sure if this is used any more..
18376             if (m.finalize) {
18377                 m.finalize.name = m.name + " (clean up) ";
18378                 mods.push(m.finalize);
18379             }
18380             
18381         }
18382         if (this.topModule && this.topModule.modules) { 
18383             this.topModule.modules.keySort('ASC',  cmp );
18384             this.topModule.modules.each(addMod);
18385         } 
18386         return mods;
18387     },
18388     
18389      /**
18390      * Build the registered modules.
18391      * @param {Object} parent element.
18392      * @param {Function} optional method to call after module has been added.
18393      * 
18394      */ 
18395    
18396     build : function(opts) 
18397     {
18398         
18399         if (typeof(opts) != 'undefined') {
18400             Roo.apply(this,opts);
18401         }
18402         
18403         this.preBuild();
18404         var mods = this.buildOrder();
18405       
18406         //this.allmods = mods;
18407         //Roo.debug && Roo.log(mods);
18408         //return;
18409         if (!mods.length) { // should not happen
18410             throw "NO modules!!!";
18411         }
18412         
18413         
18414         var msg = "Building Interface...";
18415         // flash it up as modal - so we store the mask!?
18416         if (!this.hideProgress && Roo.MessageBox) {
18417             Roo.MessageBox.show({ title: 'loading' });
18418             Roo.MessageBox.show({
18419                title: "Please wait...",
18420                msg: msg,
18421                width:450,
18422                progress:true,
18423                buttons : false,
18424                closable:false,
18425                modal: false
18426               
18427             });
18428         }
18429         var total = mods.length;
18430         
18431         var _this = this;
18432         var progressRun = function() {
18433             if (!mods.length) {
18434                 Roo.debug && Roo.log('hide?');
18435                 if (!this.hideProgress && Roo.MessageBox) {
18436                     Roo.MessageBox.hide();
18437                 }
18438                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18439                 
18440                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18441                 
18442                 // THE END...
18443                 return false;   
18444             }
18445             
18446             var m = mods.shift();
18447             
18448             
18449             Roo.debug && Roo.log(m);
18450             // not sure if this is supported any more.. - modules that are are just function
18451             if (typeof(m) == 'function') { 
18452                 m.call(this);
18453                 return progressRun.defer(10, _this);
18454             } 
18455             
18456             
18457             msg = "Building Interface " + (total  - mods.length) + 
18458                     " of " + total + 
18459                     (m.name ? (' - ' + m.name) : '');
18460                         Roo.debug && Roo.log(msg);
18461             if (!_this.hideProgress &&  Roo.MessageBox) { 
18462                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18463             }
18464             
18465          
18466             // is the module disabled?
18467             var disabled = (typeof(m.disabled) == 'function') ?
18468                 m.disabled.call(m.module.disabled) : m.disabled;    
18469             
18470             
18471             if (disabled) {
18472                 return progressRun(); // we do not update the display!
18473             }
18474             
18475             // now build 
18476             
18477                         
18478                         
18479             m.render();
18480             // it's 10 on top level, and 1 on others??? why...
18481             return progressRun.defer(10, _this);
18482              
18483         }
18484         progressRun.defer(1, _this);
18485      
18486         
18487         
18488     },
18489     /**
18490      * Overlay a set of modified strings onto a component
18491      * This is dependant on our builder exporting the strings and 'named strings' elements.
18492      * 
18493      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18494      * @param {Object} associative array of 'named' string and it's new value.
18495      * 
18496      */
18497         overlayStrings : function( component, strings )
18498     {
18499         if (typeof(component['_named_strings']) == 'undefined') {
18500             throw "ERROR: component does not have _named_strings";
18501         }
18502         for ( var k in strings ) {
18503             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18504             if (md !== false) {
18505                 component['_strings'][md] = strings[k];
18506             } else {
18507                 Roo.log('could not find named string: ' + k + ' in');
18508                 Roo.log(component);
18509             }
18510             
18511         }
18512         
18513     },
18514     
18515         
18516         /**
18517          * Event Object.
18518          *
18519          *
18520          */
18521         event: false, 
18522     /**
18523          * wrapper for event.on - aliased later..  
18524          * Typically use to register a event handler for register:
18525          *
18526          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18527          *
18528          */
18529     on : false
18530    
18531     
18532     
18533 });
18534
18535 Roo.XComponent.event = new Roo.util.Observable({
18536                 events : { 
18537                         /**
18538                          * @event register
18539                          * Fires when an Component is registered,
18540                          * set the disable property on the Component to stop registration.
18541                          * @param {Roo.XComponent} c the component being registerd.
18542                          * 
18543                          */
18544                         'register' : true,
18545             /**
18546                          * @event beforebuild
18547                          * Fires before each Component is built
18548                          * can be used to apply permissions.
18549                          * @param {Roo.XComponent} c the component being registerd.
18550                          * 
18551                          */
18552                         'beforebuild' : true,
18553                         /**
18554                          * @event buildcomplete
18555                          * Fires on the top level element when all elements have been built
18556                          * @param {Roo.XComponent} the top level component.
18557                          */
18558                         'buildcomplete' : true
18559                         
18560                 }
18561 });
18562
18563 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18564  //
18565  /**
18566  * marked - a markdown parser
18567  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18568  * https://github.com/chjj/marked
18569  */
18570
18571
18572 /**
18573  *
18574  * Roo.Markdown - is a very crude wrapper around marked..
18575  *
18576  * usage:
18577  * 
18578  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18579  * 
18580  * Note: move the sample code to the bottom of this
18581  * file before uncommenting it.
18582  *
18583  */
18584
18585 Roo.Markdown = {};
18586 Roo.Markdown.toHtml = function(text) {
18587     
18588     var c = new Roo.Markdown.marked.setOptions({
18589             renderer: new Roo.Markdown.marked.Renderer(),
18590             gfm: true,
18591             tables: true,
18592             breaks: false,
18593             pedantic: false,
18594             sanitize: false,
18595             smartLists: true,
18596             smartypants: false
18597           });
18598     // A FEW HACKS!!?
18599     
18600     text = text.replace(/\\\n/g,' ');
18601     return Roo.Markdown.marked(text);
18602 };
18603 //
18604 // converter
18605 //
18606 // Wraps all "globals" so that the only thing
18607 // exposed is makeHtml().
18608 //
18609 (function() {
18610     
18611      /**
18612          * eval:var:escape
18613          * eval:var:unescape
18614          * eval:var:replace
18615          */
18616       
18617     /**
18618      * Helpers
18619      */
18620     
18621     var escape = function (html, encode) {
18622       return html
18623         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18624         .replace(/</g, '&lt;')
18625         .replace(/>/g, '&gt;')
18626         .replace(/"/g, '&quot;')
18627         .replace(/'/g, '&#39;');
18628     }
18629     
18630     var unescape = function (html) {
18631         // explicitly match decimal, hex, and named HTML entities 
18632       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18633         n = n.toLowerCase();
18634         if (n === 'colon') { return ':'; }
18635         if (n.charAt(0) === '#') {
18636           return n.charAt(1) === 'x'
18637             ? String.fromCharCode(parseInt(n.substring(2), 16))
18638             : String.fromCharCode(+n.substring(1));
18639         }
18640         return '';
18641       });
18642     }
18643     
18644     var replace = function (regex, opt) {
18645       regex = regex.source;
18646       opt = opt || '';
18647       return function self(name, val) {
18648         if (!name) { return new RegExp(regex, opt); }
18649         val = val.source || val;
18650         val = val.replace(/(^|[^\[])\^/g, '$1');
18651         regex = regex.replace(name, val);
18652         return self;
18653       };
18654     }
18655
18656
18657          /**
18658          * eval:var:noop
18659     */
18660     var noop = function () {}
18661     noop.exec = noop;
18662     
18663          /**
18664          * eval:var:merge
18665     */
18666     var merge = function (obj) {
18667       var i = 1
18668         , target
18669         , key;
18670     
18671       for (; i < arguments.length; i++) {
18672         target = arguments[i];
18673         for (key in target) {
18674           if (Object.prototype.hasOwnProperty.call(target, key)) {
18675             obj[key] = target[key];
18676           }
18677         }
18678       }
18679     
18680       return obj;
18681     }
18682     
18683     
18684     /**
18685      * Block-Level Grammar
18686      */
18687     
18688     
18689     
18690     
18691     var block = {
18692       newline: /^\n+/,
18693       code: /^( {4}[^\n]+\n*)+/,
18694       fences: noop,
18695       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18696       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18697       nptable: noop,
18698       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18699       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18700       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18701       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18702       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18703       table: noop,
18704       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18705       text: /^[^\n]+/
18706     };
18707     
18708     block.bullet = /(?:[*+-]|\d+\.)/;
18709     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18710     block.item = replace(block.item, 'gm')
18711       (/bull/g, block.bullet)
18712       ();
18713     
18714     block.list = replace(block.list)
18715       (/bull/g, block.bullet)
18716       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18717       ('def', '\\n+(?=' + block.def.source + ')')
18718       ();
18719     
18720     block.blockquote = replace(block.blockquote)
18721       ('def', block.def)
18722       ();
18723     
18724     block._tag = '(?!(?:'
18725       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18726       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18727       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18728     
18729     block.html = replace(block.html)
18730       ('comment', /<!--[\s\S]*?-->/)
18731       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18732       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18733       (/tag/g, block._tag)
18734       ();
18735     
18736     block.paragraph = replace(block.paragraph)
18737       ('hr', block.hr)
18738       ('heading', block.heading)
18739       ('lheading', block.lheading)
18740       ('blockquote', block.blockquote)
18741       ('tag', '<' + block._tag)
18742       ('def', block.def)
18743       ();
18744     
18745     /**
18746      * Normal Block Grammar
18747      */
18748     
18749     block.normal = merge({}, block);
18750     
18751     /**
18752      * GFM Block Grammar
18753      */
18754     
18755     block.gfm = merge({}, block.normal, {
18756       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18757       paragraph: /^/,
18758       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18759     });
18760     
18761     block.gfm.paragraph = replace(block.paragraph)
18762       ('(?!', '(?!'
18763         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18764         + block.list.source.replace('\\1', '\\3') + '|')
18765       ();
18766     
18767     /**
18768      * GFM + Tables Block Grammar
18769      */
18770     
18771     block.tables = merge({}, block.gfm, {
18772       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18773       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18774     });
18775     
18776     /**
18777      * Block Lexer
18778      */
18779     
18780     var Lexer = function (options) {
18781       this.tokens = [];
18782       this.tokens.links = {};
18783       this.options = options || marked.defaults;
18784       this.rules = block.normal;
18785     
18786       if (this.options.gfm) {
18787         if (this.options.tables) {
18788           this.rules = block.tables;
18789         } else {
18790           this.rules = block.gfm;
18791         }
18792       }
18793     }
18794     
18795     /**
18796      * Expose Block Rules
18797      */
18798     
18799     Lexer.rules = block;
18800     
18801     /**
18802      * Static Lex Method
18803      */
18804     
18805     Lexer.lex = function(src, options) {
18806       var lexer = new Lexer(options);
18807       return lexer.lex(src);
18808     };
18809     
18810     /**
18811      * Preprocessing
18812      */
18813     
18814     Lexer.prototype.lex = function(src) {
18815       src = src
18816         .replace(/\r\n|\r/g, '\n')
18817         .replace(/\t/g, '    ')
18818         .replace(/\u00a0/g, ' ')
18819         .replace(/\u2424/g, '\n');
18820     
18821       return this.token(src, true);
18822     };
18823     
18824     /**
18825      * Lexing
18826      */
18827     
18828     Lexer.prototype.token = function(src, top, bq) {
18829       var src = src.replace(/^ +$/gm, '')
18830         , next
18831         , loose
18832         , cap
18833         , bull
18834         , b
18835         , item
18836         , space
18837         , i
18838         , l;
18839     
18840       while (src) {
18841         // newline
18842         if (cap = this.rules.newline.exec(src)) {
18843           src = src.substring(cap[0].length);
18844           if (cap[0].length > 1) {
18845             this.tokens.push({
18846               type: 'space'
18847             });
18848           }
18849         }
18850     
18851         // code
18852         if (cap = this.rules.code.exec(src)) {
18853           src = src.substring(cap[0].length);
18854           cap = cap[0].replace(/^ {4}/gm, '');
18855           this.tokens.push({
18856             type: 'code',
18857             text: !this.options.pedantic
18858               ? cap.replace(/\n+$/, '')
18859               : cap
18860           });
18861           continue;
18862         }
18863     
18864         // fences (gfm)
18865         if (cap = this.rules.fences.exec(src)) {
18866           src = src.substring(cap[0].length);
18867           this.tokens.push({
18868             type: 'code',
18869             lang: cap[2],
18870             text: cap[3] || ''
18871           });
18872           continue;
18873         }
18874     
18875         // heading
18876         if (cap = this.rules.heading.exec(src)) {
18877           src = src.substring(cap[0].length);
18878           this.tokens.push({
18879             type: 'heading',
18880             depth: cap[1].length,
18881             text: cap[2]
18882           });
18883           continue;
18884         }
18885     
18886         // table no leading pipe (gfm)
18887         if (top && (cap = this.rules.nptable.exec(src))) {
18888           src = src.substring(cap[0].length);
18889     
18890           item = {
18891             type: 'table',
18892             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18893             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18894             cells: cap[3].replace(/\n$/, '').split('\n')
18895           };
18896     
18897           for (i = 0; i < item.align.length; i++) {
18898             if (/^ *-+: *$/.test(item.align[i])) {
18899               item.align[i] = 'right';
18900             } else if (/^ *:-+: *$/.test(item.align[i])) {
18901               item.align[i] = 'center';
18902             } else if (/^ *:-+ *$/.test(item.align[i])) {
18903               item.align[i] = 'left';
18904             } else {
18905               item.align[i] = null;
18906             }
18907           }
18908     
18909           for (i = 0; i < item.cells.length; i++) {
18910             item.cells[i] = item.cells[i].split(/ *\| */);
18911           }
18912     
18913           this.tokens.push(item);
18914     
18915           continue;
18916         }
18917     
18918         // lheading
18919         if (cap = this.rules.lheading.exec(src)) {
18920           src = src.substring(cap[0].length);
18921           this.tokens.push({
18922             type: 'heading',
18923             depth: cap[2] === '=' ? 1 : 2,
18924             text: cap[1]
18925           });
18926           continue;
18927         }
18928     
18929         // hr
18930         if (cap = this.rules.hr.exec(src)) {
18931           src = src.substring(cap[0].length);
18932           this.tokens.push({
18933             type: 'hr'
18934           });
18935           continue;
18936         }
18937     
18938         // blockquote
18939         if (cap = this.rules.blockquote.exec(src)) {
18940           src = src.substring(cap[0].length);
18941     
18942           this.tokens.push({
18943             type: 'blockquote_start'
18944           });
18945     
18946           cap = cap[0].replace(/^ *> ?/gm, '');
18947     
18948           // Pass `top` to keep the current
18949           // "toplevel" state. This is exactly
18950           // how markdown.pl works.
18951           this.token(cap, top, true);
18952     
18953           this.tokens.push({
18954             type: 'blockquote_end'
18955           });
18956     
18957           continue;
18958         }
18959     
18960         // list
18961         if (cap = this.rules.list.exec(src)) {
18962           src = src.substring(cap[0].length);
18963           bull = cap[2];
18964     
18965           this.tokens.push({
18966             type: 'list_start',
18967             ordered: bull.length > 1
18968           });
18969     
18970           // Get each top-level item.
18971           cap = cap[0].match(this.rules.item);
18972     
18973           next = false;
18974           l = cap.length;
18975           i = 0;
18976     
18977           for (; i < l; i++) {
18978             item = cap[i];
18979     
18980             // Remove the list item's bullet
18981             // so it is seen as the next token.
18982             space = item.length;
18983             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18984     
18985             // Outdent whatever the
18986             // list item contains. Hacky.
18987             if (~item.indexOf('\n ')) {
18988               space -= item.length;
18989               item = !this.options.pedantic
18990                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18991                 : item.replace(/^ {1,4}/gm, '');
18992             }
18993     
18994             // Determine whether the next list item belongs here.
18995             // Backpedal if it does not belong in this list.
18996             if (this.options.smartLists && i !== l - 1) {
18997               b = block.bullet.exec(cap[i + 1])[0];
18998               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18999                 src = cap.slice(i + 1).join('\n') + src;
19000                 i = l - 1;
19001               }
19002             }
19003     
19004             // Determine whether item is loose or not.
19005             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
19006             // for discount behavior.
19007             loose = next || /\n\n(?!\s*$)/.test(item);
19008             if (i !== l - 1) {
19009               next = item.charAt(item.length - 1) === '\n';
19010               if (!loose) { loose = next; }
19011             }
19012     
19013             this.tokens.push({
19014               type: loose
19015                 ? 'loose_item_start'
19016                 : 'list_item_start'
19017             });
19018     
19019             // Recurse.
19020             this.token(item, false, bq);
19021     
19022             this.tokens.push({
19023               type: 'list_item_end'
19024             });
19025           }
19026     
19027           this.tokens.push({
19028             type: 'list_end'
19029           });
19030     
19031           continue;
19032         }
19033     
19034         // html
19035         if (cap = this.rules.html.exec(src)) {
19036           src = src.substring(cap[0].length);
19037           this.tokens.push({
19038             type: this.options.sanitize
19039               ? 'paragraph'
19040               : 'html',
19041             pre: !this.options.sanitizer
19042               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
19043             text: cap[0]
19044           });
19045           continue;
19046         }
19047     
19048         // def
19049         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
19050           src = src.substring(cap[0].length);
19051           this.tokens.links[cap[1].toLowerCase()] = {
19052             href: cap[2],
19053             title: cap[3]
19054           };
19055           continue;
19056         }
19057     
19058         // table (gfm)
19059         if (top && (cap = this.rules.table.exec(src))) {
19060           src = src.substring(cap[0].length);
19061     
19062           item = {
19063             type: 'table',
19064             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
19065             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
19066             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
19067           };
19068     
19069           for (i = 0; i < item.align.length; i++) {
19070             if (/^ *-+: *$/.test(item.align[i])) {
19071               item.align[i] = 'right';
19072             } else if (/^ *:-+: *$/.test(item.align[i])) {
19073               item.align[i] = 'center';
19074             } else if (/^ *:-+ *$/.test(item.align[i])) {
19075               item.align[i] = 'left';
19076             } else {
19077               item.align[i] = null;
19078             }
19079           }
19080     
19081           for (i = 0; i < item.cells.length; i++) {
19082             item.cells[i] = item.cells[i]
19083               .replace(/^ *\| *| *\| *$/g, '')
19084               .split(/ *\| */);
19085           }
19086     
19087           this.tokens.push(item);
19088     
19089           continue;
19090         }
19091     
19092         // top-level paragraph
19093         if (top && (cap = this.rules.paragraph.exec(src))) {
19094           src = src.substring(cap[0].length);
19095           this.tokens.push({
19096             type: 'paragraph',
19097             text: cap[1].charAt(cap[1].length - 1) === '\n'
19098               ? cap[1].slice(0, -1)
19099               : cap[1]
19100           });
19101           continue;
19102         }
19103     
19104         // text
19105         if (cap = this.rules.text.exec(src)) {
19106           // Top-level should never reach here.
19107           src = src.substring(cap[0].length);
19108           this.tokens.push({
19109             type: 'text',
19110             text: cap[0]
19111           });
19112           continue;
19113         }
19114     
19115         if (src) {
19116           throw new
19117             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19118         }
19119       }
19120     
19121       return this.tokens;
19122     };
19123     
19124     /**
19125      * Inline-Level Grammar
19126      */
19127     
19128     var inline = {
19129       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
19130       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
19131       url: noop,
19132       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
19133       link: /^!?\[(inside)\]\(href\)/,
19134       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
19135       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
19136       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
19137       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
19138       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
19139       br: /^ {2,}\n(?!\s*$)/,
19140       del: noop,
19141       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
19142     };
19143     
19144     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
19145     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
19146     
19147     inline.link = replace(inline.link)
19148       ('inside', inline._inside)
19149       ('href', inline._href)
19150       ();
19151     
19152     inline.reflink = replace(inline.reflink)
19153       ('inside', inline._inside)
19154       ();
19155     
19156     /**
19157      * Normal Inline Grammar
19158      */
19159     
19160     inline.normal = merge({}, inline);
19161     
19162     /**
19163      * Pedantic Inline Grammar
19164      */
19165     
19166     inline.pedantic = merge({}, inline.normal, {
19167       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
19168       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
19169     });
19170     
19171     /**
19172      * GFM Inline Grammar
19173      */
19174     
19175     inline.gfm = merge({}, inline.normal, {
19176       escape: replace(inline.escape)('])', '~|])')(),
19177       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
19178       del: /^~~(?=\S)([\s\S]*?\S)~~/,
19179       text: replace(inline.text)
19180         (']|', '~]|')
19181         ('|', '|https?://|')
19182         ()
19183     });
19184     
19185     /**
19186      * GFM + Line Breaks Inline Grammar
19187      */
19188     
19189     inline.breaks = merge({}, inline.gfm, {
19190       br: replace(inline.br)('{2,}', '*')(),
19191       text: replace(inline.gfm.text)('{2,}', '*')()
19192     });
19193     
19194     /**
19195      * Inline Lexer & Compiler
19196      */
19197     
19198     var InlineLexer  = function (links, options) {
19199       this.options = options || marked.defaults;
19200       this.links = links;
19201       this.rules = inline.normal;
19202       this.renderer = this.options.renderer || new Renderer;
19203       this.renderer.options = this.options;
19204     
19205       if (!this.links) {
19206         throw new
19207           Error('Tokens array requires a `links` property.');
19208       }
19209     
19210       if (this.options.gfm) {
19211         if (this.options.breaks) {
19212           this.rules = inline.breaks;
19213         } else {
19214           this.rules = inline.gfm;
19215         }
19216       } else if (this.options.pedantic) {
19217         this.rules = inline.pedantic;
19218       }
19219     }
19220     
19221     /**
19222      * Expose Inline Rules
19223      */
19224     
19225     InlineLexer.rules = inline;
19226     
19227     /**
19228      * Static Lexing/Compiling Method
19229      */
19230     
19231     InlineLexer.output = function(src, links, options) {
19232       var inline = new InlineLexer(links, options);
19233       return inline.output(src);
19234     };
19235     
19236     /**
19237      * Lexing/Compiling
19238      */
19239     
19240     InlineLexer.prototype.output = function(src) {
19241       var out = ''
19242         , link
19243         , text
19244         , href
19245         , cap;
19246     
19247       while (src) {
19248         // escape
19249         if (cap = this.rules.escape.exec(src)) {
19250           src = src.substring(cap[0].length);
19251           out += cap[1];
19252           continue;
19253         }
19254     
19255         // autolink
19256         if (cap = this.rules.autolink.exec(src)) {
19257           src = src.substring(cap[0].length);
19258           if (cap[2] === '@') {
19259             text = cap[1].charAt(6) === ':'
19260               ? this.mangle(cap[1].substring(7))
19261               : this.mangle(cap[1]);
19262             href = this.mangle('mailto:') + text;
19263           } else {
19264             text = escape(cap[1]);
19265             href = text;
19266           }
19267           out += this.renderer.link(href, null, text);
19268           continue;
19269         }
19270     
19271         // url (gfm)
19272         if (!this.inLink && (cap = this.rules.url.exec(src))) {
19273           src = src.substring(cap[0].length);
19274           text = escape(cap[1]);
19275           href = text;
19276           out += this.renderer.link(href, null, text);
19277           continue;
19278         }
19279     
19280         // tag
19281         if (cap = this.rules.tag.exec(src)) {
19282           if (!this.inLink && /^<a /i.test(cap[0])) {
19283             this.inLink = true;
19284           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
19285             this.inLink = false;
19286           }
19287           src = src.substring(cap[0].length);
19288           out += this.options.sanitize
19289             ? this.options.sanitizer
19290               ? this.options.sanitizer(cap[0])
19291               : escape(cap[0])
19292             : cap[0];
19293           continue;
19294         }
19295     
19296         // link
19297         if (cap = this.rules.link.exec(src)) {
19298           src = src.substring(cap[0].length);
19299           this.inLink = true;
19300           out += this.outputLink(cap, {
19301             href: cap[2],
19302             title: cap[3]
19303           });
19304           this.inLink = false;
19305           continue;
19306         }
19307     
19308         // reflink, nolink
19309         if ((cap = this.rules.reflink.exec(src))
19310             || (cap = this.rules.nolink.exec(src))) {
19311           src = src.substring(cap[0].length);
19312           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
19313           link = this.links[link.toLowerCase()];
19314           if (!link || !link.href) {
19315             out += cap[0].charAt(0);
19316             src = cap[0].substring(1) + src;
19317             continue;
19318           }
19319           this.inLink = true;
19320           out += this.outputLink(cap, link);
19321           this.inLink = false;
19322           continue;
19323         }
19324     
19325         // strong
19326         if (cap = this.rules.strong.exec(src)) {
19327           src = src.substring(cap[0].length);
19328           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19329           continue;
19330         }
19331     
19332         // em
19333         if (cap = this.rules.em.exec(src)) {
19334           src = src.substring(cap[0].length);
19335           out += this.renderer.em(this.output(cap[2] || cap[1]));
19336           continue;
19337         }
19338     
19339         // code
19340         if (cap = this.rules.code.exec(src)) {
19341           src = src.substring(cap[0].length);
19342           out += this.renderer.codespan(escape(cap[2], true));
19343           continue;
19344         }
19345     
19346         // br
19347         if (cap = this.rules.br.exec(src)) {
19348           src = src.substring(cap[0].length);
19349           out += this.renderer.br();
19350           continue;
19351         }
19352     
19353         // del (gfm)
19354         if (cap = this.rules.del.exec(src)) {
19355           src = src.substring(cap[0].length);
19356           out += this.renderer.del(this.output(cap[1]));
19357           continue;
19358         }
19359     
19360         // text
19361         if (cap = this.rules.text.exec(src)) {
19362           src = src.substring(cap[0].length);
19363           out += this.renderer.text(escape(this.smartypants(cap[0])));
19364           continue;
19365         }
19366     
19367         if (src) {
19368           throw new
19369             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19370         }
19371       }
19372     
19373       return out;
19374     };
19375     
19376     /**
19377      * Compile Link
19378      */
19379     
19380     InlineLexer.prototype.outputLink = function(cap, link) {
19381       var href = escape(link.href)
19382         , title = link.title ? escape(link.title) : null;
19383     
19384       return cap[0].charAt(0) !== '!'
19385         ? this.renderer.link(href, title, this.output(cap[1]))
19386         : this.renderer.image(href, title, escape(cap[1]));
19387     };
19388     
19389     /**
19390      * Smartypants Transformations
19391      */
19392     
19393     InlineLexer.prototype.smartypants = function(text) {
19394       if (!this.options.smartypants)  { return text; }
19395       return text
19396         // em-dashes
19397         .replace(/---/g, '\u2014')
19398         // en-dashes
19399         .replace(/--/g, '\u2013')
19400         // opening singles
19401         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19402         // closing singles & apostrophes
19403         .replace(/'/g, '\u2019')
19404         // opening doubles
19405         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19406         // closing doubles
19407         .replace(/"/g, '\u201d')
19408         // ellipses
19409         .replace(/\.{3}/g, '\u2026');
19410     };
19411     
19412     /**
19413      * Mangle Links
19414      */
19415     
19416     InlineLexer.prototype.mangle = function(text) {
19417       if (!this.options.mangle) { return text; }
19418       var out = ''
19419         , l = text.length
19420         , i = 0
19421         , ch;
19422     
19423       for (; i < l; i++) {
19424         ch = text.charCodeAt(i);
19425         if (Math.random() > 0.5) {
19426           ch = 'x' + ch.toString(16);
19427         }
19428         out += '&#' + ch + ';';
19429       }
19430     
19431       return out;
19432     };
19433     
19434     /**
19435      * Renderer
19436      */
19437     
19438      /**
19439          * eval:var:Renderer
19440     */
19441     
19442     var Renderer   = function (options) {
19443       this.options = options || {};
19444     }
19445     
19446     Renderer.prototype.code = function(code, lang, escaped) {
19447       if (this.options.highlight) {
19448         var out = this.options.highlight(code, lang);
19449         if (out != null && out !== code) {
19450           escaped = true;
19451           code = out;
19452         }
19453       } else {
19454             // hack!!! - it's already escapeD?
19455             escaped = true;
19456       }
19457     
19458       if (!lang) {
19459         return '<pre><code>'
19460           + (escaped ? code : escape(code, true))
19461           + '\n</code></pre>';
19462       }
19463     
19464       return '<pre><code class="'
19465         + this.options.langPrefix
19466         + escape(lang, true)
19467         + '">'
19468         + (escaped ? code : escape(code, true))
19469         + '\n</code></pre>\n';
19470     };
19471     
19472     Renderer.prototype.blockquote = function(quote) {
19473       return '<blockquote>\n' + quote + '</blockquote>\n';
19474     };
19475     
19476     Renderer.prototype.html = function(html) {
19477       return html;
19478     };
19479     
19480     Renderer.prototype.heading = function(text, level, raw) {
19481       return '<h'
19482         + level
19483         + ' id="'
19484         + this.options.headerPrefix
19485         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19486         + '">'
19487         + text
19488         + '</h'
19489         + level
19490         + '>\n';
19491     };
19492     
19493     Renderer.prototype.hr = function() {
19494       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19495     };
19496     
19497     Renderer.prototype.list = function(body, ordered) {
19498       var type = ordered ? 'ol' : 'ul';
19499       return '<' + type + '>\n' + body + '</' + type + '>\n';
19500     };
19501     
19502     Renderer.prototype.listitem = function(text) {
19503       return '<li>' + text + '</li>\n';
19504     };
19505     
19506     Renderer.prototype.paragraph = function(text) {
19507       return '<p>' + text + '</p>\n';
19508     };
19509     
19510     Renderer.prototype.table = function(header, body) {
19511       return '<table class="table table-striped">\n'
19512         + '<thead>\n'
19513         + header
19514         + '</thead>\n'
19515         + '<tbody>\n'
19516         + body
19517         + '</tbody>\n'
19518         + '</table>\n';
19519     };
19520     
19521     Renderer.prototype.tablerow = function(content) {
19522       return '<tr>\n' + content + '</tr>\n';
19523     };
19524     
19525     Renderer.prototype.tablecell = function(content, flags) {
19526       var type = flags.header ? 'th' : 'td';
19527       var tag = flags.align
19528         ? '<' + type + ' style="text-align:' + flags.align + '">'
19529         : '<' + type + '>';
19530       return tag + content + '</' + type + '>\n';
19531     };
19532     
19533     // span level renderer
19534     Renderer.prototype.strong = function(text) {
19535       return '<strong>' + text + '</strong>';
19536     };
19537     
19538     Renderer.prototype.em = function(text) {
19539       return '<em>' + text + '</em>';
19540     };
19541     
19542     Renderer.prototype.codespan = function(text) {
19543       return '<code>' + text + '</code>';
19544     };
19545     
19546     Renderer.prototype.br = function() {
19547       return this.options.xhtml ? '<br/>' : '<br>';
19548     };
19549     
19550     Renderer.prototype.del = function(text) {
19551       return '<del>' + text + '</del>';
19552     };
19553     
19554     Renderer.prototype.link = function(href, title, text) {
19555       if (this.options.sanitize) {
19556         try {
19557           var prot = decodeURIComponent(unescape(href))
19558             .replace(/[^\w:]/g, '')
19559             .toLowerCase();
19560         } catch (e) {
19561           return '';
19562         }
19563         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19564           return '';
19565         }
19566       }
19567       var out = '<a href="' + href + '"';
19568       if (title) {
19569         out += ' title="' + title + '"';
19570       }
19571       out += '>' + text + '</a>';
19572       return out;
19573     };
19574     
19575     Renderer.prototype.image = function(href, title, text) {
19576       var out = '<img src="' + href + '" alt="' + text + '"';
19577       if (title) {
19578         out += ' title="' + title + '"';
19579       }
19580       out += this.options.xhtml ? '/>' : '>';
19581       return out;
19582     };
19583     
19584     Renderer.prototype.text = function(text) {
19585       return text;
19586     };
19587     
19588     /**
19589      * Parsing & Compiling
19590      */
19591          /**
19592          * eval:var:Parser
19593     */
19594     
19595     var Parser= function (options) {
19596       this.tokens = [];
19597       this.token = null;
19598       this.options = options || marked.defaults;
19599       this.options.renderer = this.options.renderer || new Renderer;
19600       this.renderer = this.options.renderer;
19601       this.renderer.options = this.options;
19602     }
19603     
19604     /**
19605      * Static Parse Method
19606      */
19607     
19608     Parser.parse = function(src, options, renderer) {
19609       var parser = new Parser(options, renderer);
19610       return parser.parse(src);
19611     };
19612     
19613     /**
19614      * Parse Loop
19615      */
19616     
19617     Parser.prototype.parse = function(src) {
19618       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19619       this.tokens = src.reverse();
19620     
19621       var out = '';
19622       while (this.next()) {
19623         out += this.tok();
19624       }
19625     
19626       return out;
19627     };
19628     
19629     /**
19630      * Next Token
19631      */
19632     
19633     Parser.prototype.next = function() {
19634       return this.token = this.tokens.pop();
19635     };
19636     
19637     /**
19638      * Preview Next Token
19639      */
19640     
19641     Parser.prototype.peek = function() {
19642       return this.tokens[this.tokens.length - 1] || 0;
19643     };
19644     
19645     /**
19646      * Parse Text Tokens
19647      */
19648     
19649     Parser.prototype.parseText = function() {
19650       var body = this.token.text;
19651     
19652       while (this.peek().type === 'text') {
19653         body += '\n' + this.next().text;
19654       }
19655     
19656       return this.inline.output(body);
19657     };
19658     
19659     /**
19660      * Parse Current Token
19661      */
19662     
19663     Parser.prototype.tok = function() {
19664       switch (this.token.type) {
19665         case 'space': {
19666           return '';
19667         }
19668         case 'hr': {
19669           return this.renderer.hr();
19670         }
19671         case 'heading': {
19672           return this.renderer.heading(
19673             this.inline.output(this.token.text),
19674             this.token.depth,
19675             this.token.text);
19676         }
19677         case 'code': {
19678           return this.renderer.code(this.token.text,
19679             this.token.lang,
19680             this.token.escaped);
19681         }
19682         case 'table': {
19683           var header = ''
19684             , body = ''
19685             , i
19686             , row
19687             , cell
19688             , flags
19689             , j;
19690     
19691           // header
19692           cell = '';
19693           for (i = 0; i < this.token.header.length; i++) {
19694             flags = { header: true, align: this.token.align[i] };
19695             cell += this.renderer.tablecell(
19696               this.inline.output(this.token.header[i]),
19697               { header: true, align: this.token.align[i] }
19698             );
19699           }
19700           header += this.renderer.tablerow(cell);
19701     
19702           for (i = 0; i < this.token.cells.length; i++) {
19703             row = this.token.cells[i];
19704     
19705             cell = '';
19706             for (j = 0; j < row.length; j++) {
19707               cell += this.renderer.tablecell(
19708                 this.inline.output(row[j]),
19709                 { header: false, align: this.token.align[j] }
19710               );
19711             }
19712     
19713             body += this.renderer.tablerow(cell);
19714           }
19715           return this.renderer.table(header, body);
19716         }
19717         case 'blockquote_start': {
19718           var body = '';
19719     
19720           while (this.next().type !== 'blockquote_end') {
19721             body += this.tok();
19722           }
19723     
19724           return this.renderer.blockquote(body);
19725         }
19726         case 'list_start': {
19727           var body = ''
19728             , ordered = this.token.ordered;
19729     
19730           while (this.next().type !== 'list_end') {
19731             body += this.tok();
19732           }
19733     
19734           return this.renderer.list(body, ordered);
19735         }
19736         case 'list_item_start': {
19737           var body = '';
19738     
19739           while (this.next().type !== 'list_item_end') {
19740             body += this.token.type === 'text'
19741               ? this.parseText()
19742               : this.tok();
19743           }
19744     
19745           return this.renderer.listitem(body);
19746         }
19747         case 'loose_item_start': {
19748           var body = '';
19749     
19750           while (this.next().type !== 'list_item_end') {
19751             body += this.tok();
19752           }
19753     
19754           return this.renderer.listitem(body);
19755         }
19756         case 'html': {
19757           var html = !this.token.pre && !this.options.pedantic
19758             ? this.inline.output(this.token.text)
19759             : this.token.text;
19760           return this.renderer.html(html);
19761         }
19762         case 'paragraph': {
19763           return this.renderer.paragraph(this.inline.output(this.token.text));
19764         }
19765         case 'text': {
19766           return this.renderer.paragraph(this.parseText());
19767         }
19768       }
19769     };
19770   
19771     
19772     /**
19773      * Marked
19774      */
19775          /**
19776          * eval:var:marked
19777     */
19778     var marked = function (src, opt, callback) {
19779       if (callback || typeof opt === 'function') {
19780         if (!callback) {
19781           callback = opt;
19782           opt = null;
19783         }
19784     
19785         opt = merge({}, marked.defaults, opt || {});
19786     
19787         var highlight = opt.highlight
19788           , tokens
19789           , pending
19790           , i = 0;
19791     
19792         try {
19793           tokens = Lexer.lex(src, opt)
19794         } catch (e) {
19795           return callback(e);
19796         }
19797     
19798         pending = tokens.length;
19799          /**
19800          * eval:var:done
19801     */
19802         var done = function(err) {
19803           if (err) {
19804             opt.highlight = highlight;
19805             return callback(err);
19806           }
19807     
19808           var out;
19809     
19810           try {
19811             out = Parser.parse(tokens, opt);
19812           } catch (e) {
19813             err = e;
19814           }
19815     
19816           opt.highlight = highlight;
19817     
19818           return err
19819             ? callback(err)
19820             : callback(null, out);
19821         };
19822     
19823         if (!highlight || highlight.length < 3) {
19824           return done();
19825         }
19826     
19827         delete opt.highlight;
19828     
19829         if (!pending) { return done(); }
19830     
19831         for (; i < tokens.length; i++) {
19832           (function(token) {
19833             if (token.type !== 'code') {
19834               return --pending || done();
19835             }
19836             return highlight(token.text, token.lang, function(err, code) {
19837               if (err) { return done(err); }
19838               if (code == null || code === token.text) {
19839                 return --pending || done();
19840               }
19841               token.text = code;
19842               token.escaped = true;
19843               --pending || done();
19844             });
19845           })(tokens[i]);
19846         }
19847     
19848         return;
19849       }
19850       try {
19851         if (opt) { opt = merge({}, marked.defaults, opt); }
19852         return Parser.parse(Lexer.lex(src, opt), opt);
19853       } catch (e) {
19854         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19855         if ((opt || marked.defaults).silent) {
19856           return '<p>An error occured:</p><pre>'
19857             + escape(e.message + '', true)
19858             + '</pre>';
19859         }
19860         throw e;
19861       }
19862     }
19863     
19864     /**
19865      * Options
19866      */
19867     
19868     marked.options =
19869     marked.setOptions = function(opt) {
19870       merge(marked.defaults, opt);
19871       return marked;
19872     };
19873     
19874     marked.defaults = {
19875       gfm: true,
19876       tables: true,
19877       breaks: false,
19878       pedantic: false,
19879       sanitize: false,
19880       sanitizer: null,
19881       mangle: true,
19882       smartLists: false,
19883       silent: false,
19884       highlight: null,
19885       langPrefix: 'lang-',
19886       smartypants: false,
19887       headerPrefix: '',
19888       renderer: new Renderer,
19889       xhtml: false
19890     };
19891     
19892     /**
19893      * Expose
19894      */
19895     
19896     marked.Parser = Parser;
19897     marked.parser = Parser.parse;
19898     
19899     marked.Renderer = Renderer;
19900     
19901     marked.Lexer = Lexer;
19902     marked.lexer = Lexer.lex;
19903     
19904     marked.InlineLexer = InlineLexer;
19905     marked.inlineLexer = InlineLexer.output;
19906     
19907     marked.parse = marked;
19908     
19909     Roo.Markdown.marked = marked;
19910
19911 })();/*
19912  * Based on:
19913  * Ext JS Library 1.1.1
19914  * Copyright(c) 2006-2007, Ext JS, LLC.
19915  *
19916  * Originally Released Under LGPL - original licence link has changed is not relivant.
19917  *
19918  * Fork - LGPL
19919  * <script type="text/javascript">
19920  */
19921
19922
19923
19924 /*
19925  * These classes are derivatives of the similarly named classes in the YUI Library.
19926  * The original license:
19927  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19928  * Code licensed under the BSD License:
19929  * http://developer.yahoo.net/yui/license.txt
19930  */
19931
19932 (function() {
19933
19934 var Event=Roo.EventManager;
19935 var Dom=Roo.lib.Dom;
19936
19937 /**
19938  * @class Roo.dd.DragDrop
19939  * @extends Roo.util.Observable
19940  * Defines the interface and base operation of items that that can be
19941  * dragged or can be drop targets.  It was designed to be extended, overriding
19942  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19943  * Up to three html elements can be associated with a DragDrop instance:
19944  * <ul>
19945  * <li>linked element: the element that is passed into the constructor.
19946  * This is the element which defines the boundaries for interaction with
19947  * other DragDrop objects.</li>
19948  * <li>handle element(s): The drag operation only occurs if the element that
19949  * was clicked matches a handle element.  By default this is the linked
19950  * element, but there are times that you will want only a portion of the
19951  * linked element to initiate the drag operation, and the setHandleElId()
19952  * method provides a way to define this.</li>
19953  * <li>drag element: this represents the element that would be moved along
19954  * with the cursor during a drag operation.  By default, this is the linked
19955  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19956  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19957  * </li>
19958  * </ul>
19959  * This class should not be instantiated until the onload event to ensure that
19960  * the associated elements are available.
19961  * The following would define a DragDrop obj that would interact with any
19962  * other DragDrop obj in the "group1" group:
19963  * <pre>
19964  *  dd = new Roo.dd.DragDrop("div1", "group1");
19965  * </pre>
19966  * Since none of the event handlers have been implemented, nothing would
19967  * actually happen if you were to run the code above.  Normally you would
19968  * override this class or one of the default implementations, but you can
19969  * also override the methods you want on an instance of the class...
19970  * <pre>
19971  *  dd.onDragDrop = function(e, id) {
19972  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19973  *  }
19974  * </pre>
19975  * @constructor
19976  * @param {String} id of the element that is linked to this instance
19977  * @param {String} sGroup the group of related DragDrop objects
19978  * @param {object} config an object containing configurable attributes
19979  *                Valid properties for DragDrop:
19980  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19981  */
19982 Roo.dd.DragDrop = function(id, sGroup, config) {
19983     if (id) {
19984         this.init(id, sGroup, config);
19985     }
19986     
19987 };
19988
19989 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19990
19991     /**
19992      * The id of the element associated with this object.  This is what we
19993      * refer to as the "linked element" because the size and position of
19994      * this element is used to determine when the drag and drop objects have
19995      * interacted.
19996      * @property id
19997      * @type String
19998      */
19999     id: null,
20000
20001     /**
20002      * Configuration attributes passed into the constructor
20003      * @property config
20004      * @type object
20005      */
20006     config: null,
20007
20008     /**
20009      * The id of the element that will be dragged.  By default this is same
20010      * as the linked element , but could be changed to another element. Ex:
20011      * Roo.dd.DDProxy
20012      * @property dragElId
20013      * @type String
20014      * @private
20015      */
20016     dragElId: null,
20017
20018     /**
20019      * the id of the element that initiates the drag operation.  By default
20020      * this is the linked element, but could be changed to be a child of this
20021      * element.  This lets us do things like only starting the drag when the
20022      * header element within the linked html element is clicked.
20023      * @property handleElId
20024      * @type String
20025      * @private
20026      */
20027     handleElId: null,
20028
20029     /**
20030      * An associative array of HTML tags that will be ignored if clicked.
20031      * @property invalidHandleTypes
20032      * @type {string: string}
20033      */
20034     invalidHandleTypes: null,
20035
20036     /**
20037      * An associative array of ids for elements that will be ignored if clicked
20038      * @property invalidHandleIds
20039      * @type {string: string}
20040      */
20041     invalidHandleIds: null,
20042
20043     /**
20044      * An indexted array of css class names for elements that will be ignored
20045      * if clicked.
20046      * @property invalidHandleClasses
20047      * @type string[]
20048      */
20049     invalidHandleClasses: null,
20050
20051     /**
20052      * The linked element's absolute X position at the time the drag was
20053      * started
20054      * @property startPageX
20055      * @type int
20056      * @private
20057      */
20058     startPageX: 0,
20059
20060     /**
20061      * The linked element's absolute X position at the time the drag was
20062      * started
20063      * @property startPageY
20064      * @type int
20065      * @private
20066      */
20067     startPageY: 0,
20068
20069     /**
20070      * The group defines a logical collection of DragDrop objects that are
20071      * related.  Instances only get events when interacting with other
20072      * DragDrop object in the same group.  This lets us define multiple
20073      * groups using a single DragDrop subclass if we want.
20074      * @property groups
20075      * @type {string: string}
20076      */
20077     groups: null,
20078
20079     /**
20080      * Individual drag/drop instances can be locked.  This will prevent
20081      * onmousedown start drag.
20082      * @property locked
20083      * @type boolean
20084      * @private
20085      */
20086     locked: false,
20087
20088     /**
20089      * Lock this instance
20090      * @method lock
20091      */
20092     lock: function() { this.locked = true; },
20093
20094     /**
20095      * Unlock this instace
20096      * @method unlock
20097      */
20098     unlock: function() { this.locked = false; },
20099
20100     /**
20101      * By default, all insances can be a drop target.  This can be disabled by
20102      * setting isTarget to false.
20103      * @method isTarget
20104      * @type boolean
20105      */
20106     isTarget: true,
20107
20108     /**
20109      * The padding configured for this drag and drop object for calculating
20110      * the drop zone intersection with this object.
20111      * @method padding
20112      * @type int[]
20113      */
20114     padding: null,
20115
20116     /**
20117      * Cached reference to the linked element
20118      * @property _domRef
20119      * @private
20120      */
20121     _domRef: null,
20122
20123     /**
20124      * Internal typeof flag
20125      * @property __ygDragDrop
20126      * @private
20127      */
20128     __ygDragDrop: true,
20129
20130     /**
20131      * Set to true when horizontal contraints are applied
20132      * @property constrainX
20133      * @type boolean
20134      * @private
20135      */
20136     constrainX: false,
20137
20138     /**
20139      * Set to true when vertical contraints are applied
20140      * @property constrainY
20141      * @type boolean
20142      * @private
20143      */
20144     constrainY: false,
20145
20146     /**
20147      * The left constraint
20148      * @property minX
20149      * @type int
20150      * @private
20151      */
20152     minX: 0,
20153
20154     /**
20155      * The right constraint
20156      * @property maxX
20157      * @type int
20158      * @private
20159      */
20160     maxX: 0,
20161
20162     /**
20163      * The up constraint
20164      * @property minY
20165      * @type int
20166      * @type int
20167      * @private
20168      */
20169     minY: 0,
20170
20171     /**
20172      * The down constraint
20173      * @property maxY
20174      * @type int
20175      * @private
20176      */
20177     maxY: 0,
20178
20179     /**
20180      * Maintain offsets when we resetconstraints.  Set to true when you want
20181      * the position of the element relative to its parent to stay the same
20182      * when the page changes
20183      *
20184      * @property maintainOffset
20185      * @type boolean
20186      */
20187     maintainOffset: false,
20188
20189     /**
20190      * Array of pixel locations the element will snap to if we specified a
20191      * horizontal graduation/interval.  This array is generated automatically
20192      * when you define a tick interval.
20193      * @property xTicks
20194      * @type int[]
20195      */
20196     xTicks: null,
20197
20198     /**
20199      * Array of pixel locations the element will snap to if we specified a
20200      * vertical graduation/interval.  This array is generated automatically
20201      * when you define a tick interval.
20202      * @property yTicks
20203      * @type int[]
20204      */
20205     yTicks: null,
20206
20207     /**
20208      * By default the drag and drop instance will only respond to the primary
20209      * button click (left button for a right-handed mouse).  Set to true to
20210      * allow drag and drop to start with any mouse click that is propogated
20211      * by the browser
20212      * @property primaryButtonOnly
20213      * @type boolean
20214      */
20215     primaryButtonOnly: true,
20216
20217     /**
20218      * The availabe property is false until the linked dom element is accessible.
20219      * @property available
20220      * @type boolean
20221      */
20222     available: false,
20223
20224     /**
20225      * By default, drags can only be initiated if the mousedown occurs in the
20226      * region the linked element is.  This is done in part to work around a
20227      * bug in some browsers that mis-report the mousedown if the previous
20228      * mouseup happened outside of the window.  This property is set to true
20229      * if outer handles are defined.
20230      *
20231      * @property hasOuterHandles
20232      * @type boolean
20233      * @default false
20234      */
20235     hasOuterHandles: false,
20236
20237     /**
20238      * Code that executes immediately before the startDrag event
20239      * @method b4StartDrag
20240      * @private
20241      */
20242     b4StartDrag: function(x, y) { },
20243
20244     /**
20245      * Abstract method called after a drag/drop object is clicked
20246      * and the drag or mousedown time thresholds have beeen met.
20247      * @method startDrag
20248      * @param {int} X click location
20249      * @param {int} Y click location
20250      */
20251     startDrag: function(x, y) { /* override this */ },
20252
20253     /**
20254      * Code that executes immediately before the onDrag event
20255      * @method b4Drag
20256      * @private
20257      */
20258     b4Drag: function(e) { },
20259
20260     /**
20261      * Abstract method called during the onMouseMove event while dragging an
20262      * object.
20263      * @method onDrag
20264      * @param {Event} e the mousemove event
20265      */
20266     onDrag: function(e) { /* override this */ },
20267
20268     /**
20269      * Abstract method called when this element fist begins hovering over
20270      * another DragDrop obj
20271      * @method onDragEnter
20272      * @param {Event} e the mousemove event
20273      * @param {String|DragDrop[]} id In POINT mode, the element
20274      * id this is hovering over.  In INTERSECT mode, an array of one or more
20275      * dragdrop items being hovered over.
20276      */
20277     onDragEnter: function(e, id) { /* override this */ },
20278
20279     /**
20280      * Code that executes immediately before the onDragOver event
20281      * @method b4DragOver
20282      * @private
20283      */
20284     b4DragOver: function(e) { },
20285
20286     /**
20287      * Abstract method called when this element is hovering over another
20288      * DragDrop obj
20289      * @method onDragOver
20290      * @param {Event} e the mousemove event
20291      * @param {String|DragDrop[]} id In POINT mode, the element
20292      * id this is hovering over.  In INTERSECT mode, an array of dd items
20293      * being hovered over.
20294      */
20295     onDragOver: function(e, id) { /* override this */ },
20296
20297     /**
20298      * Code that executes immediately before the onDragOut event
20299      * @method b4DragOut
20300      * @private
20301      */
20302     b4DragOut: function(e) { },
20303
20304     /**
20305      * Abstract method called when we are no longer hovering over an element
20306      * @method onDragOut
20307      * @param {Event} e the mousemove event
20308      * @param {String|DragDrop[]} id In POINT mode, the element
20309      * id this was hovering over.  In INTERSECT mode, an array of dd items
20310      * that the mouse is no longer over.
20311      */
20312     onDragOut: function(e, id) { /* override this */ },
20313
20314     /**
20315      * Code that executes immediately before the onDragDrop event
20316      * @method b4DragDrop
20317      * @private
20318      */
20319     b4DragDrop: function(e) { },
20320
20321     /**
20322      * Abstract method called when this item is dropped on another DragDrop
20323      * obj
20324      * @method onDragDrop
20325      * @param {Event} e the mouseup event
20326      * @param {String|DragDrop[]} id In POINT mode, the element
20327      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20328      * was dropped on.
20329      */
20330     onDragDrop: function(e, id) { /* override this */ },
20331
20332     /**
20333      * Abstract method called when this item is dropped on an area with no
20334      * drop target
20335      * @method onInvalidDrop
20336      * @param {Event} e the mouseup event
20337      */
20338     onInvalidDrop: function(e) { /* override this */ },
20339
20340     /**
20341      * Code that executes immediately before the endDrag event
20342      * @method b4EndDrag
20343      * @private
20344      */
20345     b4EndDrag: function(e) { },
20346
20347     /**
20348      * Fired when we are done dragging the object
20349      * @method endDrag
20350      * @param {Event} e the mouseup event
20351      */
20352     endDrag: function(e) { /* override this */ },
20353
20354     /**
20355      * Code executed immediately before the onMouseDown event
20356      * @method b4MouseDown
20357      * @param {Event} e the mousedown event
20358      * @private
20359      */
20360     b4MouseDown: function(e) {  },
20361
20362     /**
20363      * Event handler that fires when a drag/drop obj gets a mousedown
20364      * @method onMouseDown
20365      * @param {Event} e the mousedown event
20366      */
20367     onMouseDown: function(e) { /* override this */ },
20368
20369     /**
20370      * Event handler that fires when a drag/drop obj gets a mouseup
20371      * @method onMouseUp
20372      * @param {Event} e the mouseup event
20373      */
20374     onMouseUp: function(e) { /* override this */ },
20375
20376     /**
20377      * Override the onAvailable method to do what is needed after the initial
20378      * position was determined.
20379      * @method onAvailable
20380      */
20381     onAvailable: function () {
20382     },
20383
20384     /*
20385      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20386      * @type Object
20387      */
20388     defaultPadding : {left:0, right:0, top:0, bottom:0},
20389
20390     /*
20391      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20392  *
20393  * Usage:
20394  <pre><code>
20395  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20396                 { dragElId: "existingProxyDiv" });
20397  dd.startDrag = function(){
20398      this.constrainTo("parent-id");
20399  };
20400  </code></pre>
20401  * Or you can initalize it using the {@link Roo.Element} object:
20402  <pre><code>
20403  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20404      startDrag : function(){
20405          this.constrainTo("parent-id");
20406      }
20407  });
20408  </code></pre>
20409      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20410      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20411      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20412      * an object containing the sides to pad. For example: {right:10, bottom:10}
20413      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20414      */
20415     constrainTo : function(constrainTo, pad, inContent){
20416         if(typeof pad == "number"){
20417             pad = {left: pad, right:pad, top:pad, bottom:pad};
20418         }
20419         pad = pad || this.defaultPadding;
20420         var b = Roo.get(this.getEl()).getBox();
20421         var ce = Roo.get(constrainTo);
20422         var s = ce.getScroll();
20423         var c, cd = ce.dom;
20424         if(cd == document.body){
20425             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20426         }else{
20427             xy = ce.getXY();
20428             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20429         }
20430
20431
20432         var topSpace = b.y - c.y;
20433         var leftSpace = b.x - c.x;
20434
20435         this.resetConstraints();
20436         this.setXConstraint(leftSpace - (pad.left||0), // left
20437                 c.width - leftSpace - b.width - (pad.right||0) //right
20438         );
20439         this.setYConstraint(topSpace - (pad.top||0), //top
20440                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20441         );
20442     },
20443
20444     /**
20445      * Returns a reference to the linked element
20446      * @method getEl
20447      * @return {HTMLElement} the html element
20448      */
20449     getEl: function() {
20450         if (!this._domRef) {
20451             this._domRef = Roo.getDom(this.id);
20452         }
20453
20454         return this._domRef;
20455     },
20456
20457     /**
20458      * Returns a reference to the actual element to drag.  By default this is
20459      * the same as the html element, but it can be assigned to another
20460      * element. An example of this can be found in Roo.dd.DDProxy
20461      * @method getDragEl
20462      * @return {HTMLElement} the html element
20463      */
20464     getDragEl: function() {
20465         return Roo.getDom(this.dragElId);
20466     },
20467
20468     /**
20469      * Sets up the DragDrop object.  Must be called in the constructor of any
20470      * Roo.dd.DragDrop subclass
20471      * @method init
20472      * @param id the id of the linked element
20473      * @param {String} sGroup the group of related items
20474      * @param {object} config configuration attributes
20475      */
20476     init: function(id, sGroup, config) {
20477         this.initTarget(id, sGroup, config);
20478         if (!Roo.isTouch) {
20479             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20480         }
20481         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20482         // Event.on(this.id, "selectstart", Event.preventDefault);
20483     },
20484
20485     /**
20486      * Initializes Targeting functionality only... the object does not
20487      * get a mousedown handler.
20488      * @method initTarget
20489      * @param id the id of the linked element
20490      * @param {String} sGroup the group of related items
20491      * @param {object} config configuration attributes
20492      */
20493     initTarget: function(id, sGroup, config) {
20494
20495         // configuration attributes
20496         this.config = config || {};
20497
20498         // create a local reference to the drag and drop manager
20499         this.DDM = Roo.dd.DDM;
20500         // initialize the groups array
20501         this.groups = {};
20502
20503         // assume that we have an element reference instead of an id if the
20504         // parameter is not a string
20505         if (typeof id !== "string") {
20506             id = Roo.id(id);
20507         }
20508
20509         // set the id
20510         this.id = id;
20511
20512         // add to an interaction group
20513         this.addToGroup((sGroup) ? sGroup : "default");
20514
20515         // We don't want to register this as the handle with the manager
20516         // so we just set the id rather than calling the setter.
20517         this.handleElId = id;
20518
20519         // the linked element is the element that gets dragged by default
20520         this.setDragElId(id);
20521
20522         // by default, clicked anchors will not start drag operations.
20523         this.invalidHandleTypes = { A: "A" };
20524         this.invalidHandleIds = {};
20525         this.invalidHandleClasses = [];
20526
20527         this.applyConfig();
20528
20529         this.handleOnAvailable();
20530     },
20531
20532     /**
20533      * Applies the configuration parameters that were passed into the constructor.
20534      * This is supposed to happen at each level through the inheritance chain.  So
20535      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20536      * DragDrop in order to get all of the parameters that are available in
20537      * each object.
20538      * @method applyConfig
20539      */
20540     applyConfig: function() {
20541
20542         // configurable properties:
20543         //    padding, isTarget, maintainOffset, primaryButtonOnly
20544         this.padding           = this.config.padding || [0, 0, 0, 0];
20545         this.isTarget          = (this.config.isTarget !== false);
20546         this.maintainOffset    = (this.config.maintainOffset);
20547         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20548
20549     },
20550
20551     /**
20552      * Executed when the linked element is available
20553      * @method handleOnAvailable
20554      * @private
20555      */
20556     handleOnAvailable: function() {
20557         this.available = true;
20558         this.resetConstraints();
20559         this.onAvailable();
20560     },
20561
20562      /**
20563      * Configures the padding for the target zone in px.  Effectively expands
20564      * (or reduces) the virtual object size for targeting calculations.
20565      * Supports css-style shorthand; if only one parameter is passed, all sides
20566      * will have that padding, and if only two are passed, the top and bottom
20567      * will have the first param, the left and right the second.
20568      * @method setPadding
20569      * @param {int} iTop    Top pad
20570      * @param {int} iRight  Right pad
20571      * @param {int} iBot    Bot pad
20572      * @param {int} iLeft   Left pad
20573      */
20574     setPadding: function(iTop, iRight, iBot, iLeft) {
20575         // this.padding = [iLeft, iRight, iTop, iBot];
20576         if (!iRight && 0 !== iRight) {
20577             this.padding = [iTop, iTop, iTop, iTop];
20578         } else if (!iBot && 0 !== iBot) {
20579             this.padding = [iTop, iRight, iTop, iRight];
20580         } else {
20581             this.padding = [iTop, iRight, iBot, iLeft];
20582         }
20583     },
20584
20585     /**
20586      * Stores the initial placement of the linked element.
20587      * @method setInitialPosition
20588      * @param {int} diffX   the X offset, default 0
20589      * @param {int} diffY   the Y offset, default 0
20590      */
20591     setInitPosition: function(diffX, diffY) {
20592         var el = this.getEl();
20593
20594         if (!this.DDM.verifyEl(el)) {
20595             return;
20596         }
20597
20598         var dx = diffX || 0;
20599         var dy = diffY || 0;
20600
20601         var p = Dom.getXY( el );
20602
20603         this.initPageX = p[0] - dx;
20604         this.initPageY = p[1] - dy;
20605
20606         this.lastPageX = p[0];
20607         this.lastPageY = p[1];
20608
20609
20610         this.setStartPosition(p);
20611     },
20612
20613     /**
20614      * Sets the start position of the element.  This is set when the obj
20615      * is initialized, the reset when a drag is started.
20616      * @method setStartPosition
20617      * @param pos current position (from previous lookup)
20618      * @private
20619      */
20620     setStartPosition: function(pos) {
20621         var p = pos || Dom.getXY( this.getEl() );
20622         this.deltaSetXY = null;
20623
20624         this.startPageX = p[0];
20625         this.startPageY = p[1];
20626     },
20627
20628     /**
20629      * Add this instance to a group of related drag/drop objects.  All
20630      * instances belong to at least one group, and can belong to as many
20631      * groups as needed.
20632      * @method addToGroup
20633      * @param sGroup {string} the name of the group
20634      */
20635     addToGroup: function(sGroup) {
20636         this.groups[sGroup] = true;
20637         this.DDM.regDragDrop(this, sGroup);
20638     },
20639
20640     /**
20641      * Remove's this instance from the supplied interaction group
20642      * @method removeFromGroup
20643      * @param {string}  sGroup  The group to drop
20644      */
20645     removeFromGroup: function(sGroup) {
20646         if (this.groups[sGroup]) {
20647             delete this.groups[sGroup];
20648         }
20649
20650         this.DDM.removeDDFromGroup(this, sGroup);
20651     },
20652
20653     /**
20654      * Allows you to specify that an element other than the linked element
20655      * will be moved with the cursor during a drag
20656      * @method setDragElId
20657      * @param id {string} the id of the element that will be used to initiate the drag
20658      */
20659     setDragElId: function(id) {
20660         this.dragElId = id;
20661     },
20662
20663     /**
20664      * Allows you to specify a child of the linked element that should be
20665      * used to initiate the drag operation.  An example of this would be if
20666      * you have a content div with text and links.  Clicking anywhere in the
20667      * content area would normally start the drag operation.  Use this method
20668      * to specify that an element inside of the content div is the element
20669      * that starts the drag operation.
20670      * @method setHandleElId
20671      * @param id {string} the id of the element that will be used to
20672      * initiate the drag.
20673      */
20674     setHandleElId: function(id) {
20675         if (typeof id !== "string") {
20676             id = Roo.id(id);
20677         }
20678         this.handleElId = id;
20679         this.DDM.regHandle(this.id, id);
20680     },
20681
20682     /**
20683      * Allows you to set an element outside of the linked element as a drag
20684      * handle
20685      * @method setOuterHandleElId
20686      * @param id the id of the element that will be used to initiate the drag
20687      */
20688     setOuterHandleElId: function(id) {
20689         if (typeof id !== "string") {
20690             id = Roo.id(id);
20691         }
20692         Event.on(id, "mousedown",
20693                 this.handleMouseDown, this);
20694         this.setHandleElId(id);
20695
20696         this.hasOuterHandles = true;
20697     },
20698
20699     /**
20700      * Remove all drag and drop hooks for this element
20701      * @method unreg
20702      */
20703     unreg: function() {
20704         Event.un(this.id, "mousedown",
20705                 this.handleMouseDown);
20706         Event.un(this.id, "touchstart",
20707                 this.handleMouseDown);
20708         this._domRef = null;
20709         this.DDM._remove(this);
20710     },
20711
20712     destroy : function(){
20713         this.unreg();
20714     },
20715
20716     /**
20717      * Returns true if this instance is locked, or the drag drop mgr is locked
20718      * (meaning that all drag/drop is disabled on the page.)
20719      * @method isLocked
20720      * @return {boolean} true if this obj or all drag/drop is locked, else
20721      * false
20722      */
20723     isLocked: function() {
20724         return (this.DDM.isLocked() || this.locked);
20725     },
20726
20727     /**
20728      * Fired when this object is clicked
20729      * @method handleMouseDown
20730      * @param {Event} e
20731      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20732      * @private
20733      */
20734     handleMouseDown: function(e, oDD){
20735      
20736         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20737             //Roo.log('not touch/ button !=0');
20738             return;
20739         }
20740         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20741             return; // double touch..
20742         }
20743         
20744
20745         if (this.isLocked()) {
20746             //Roo.log('locked');
20747             return;
20748         }
20749
20750         this.DDM.refreshCache(this.groups);
20751 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20752         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20753         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20754             //Roo.log('no outer handes or not over target');
20755                 // do nothing.
20756         } else {
20757 //            Roo.log('check validator');
20758             if (this.clickValidator(e)) {
20759 //                Roo.log('validate success');
20760                 // set the initial element position
20761                 this.setStartPosition();
20762
20763
20764                 this.b4MouseDown(e);
20765                 this.onMouseDown(e);
20766
20767                 this.DDM.handleMouseDown(e, this);
20768
20769                 this.DDM.stopEvent(e);
20770             } else {
20771
20772
20773             }
20774         }
20775     },
20776
20777     clickValidator: function(e) {
20778         var target = e.getTarget();
20779         return ( this.isValidHandleChild(target) &&
20780                     (this.id == this.handleElId ||
20781                         this.DDM.handleWasClicked(target, this.id)) );
20782     },
20783
20784     /**
20785      * Allows you to specify a tag name that should not start a drag operation
20786      * when clicked.  This is designed to facilitate embedding links within a
20787      * drag handle that do something other than start the drag.
20788      * @method addInvalidHandleType
20789      * @param {string} tagName the type of element to exclude
20790      */
20791     addInvalidHandleType: function(tagName) {
20792         var type = tagName.toUpperCase();
20793         this.invalidHandleTypes[type] = type;
20794     },
20795
20796     /**
20797      * Lets you to specify an element id for a child of a drag handle
20798      * that should not initiate a drag
20799      * @method addInvalidHandleId
20800      * @param {string} id the element id of the element you wish to ignore
20801      */
20802     addInvalidHandleId: function(id) {
20803         if (typeof id !== "string") {
20804             id = Roo.id(id);
20805         }
20806         this.invalidHandleIds[id] = id;
20807     },
20808
20809     /**
20810      * Lets you specify a css class of elements that will not initiate a drag
20811      * @method addInvalidHandleClass
20812      * @param {string} cssClass the class of the elements you wish to ignore
20813      */
20814     addInvalidHandleClass: function(cssClass) {
20815         this.invalidHandleClasses.push(cssClass);
20816     },
20817
20818     /**
20819      * Unsets an excluded tag name set by addInvalidHandleType
20820      * @method removeInvalidHandleType
20821      * @param {string} tagName the type of element to unexclude
20822      */
20823     removeInvalidHandleType: function(tagName) {
20824         var type = tagName.toUpperCase();
20825         // this.invalidHandleTypes[type] = null;
20826         delete this.invalidHandleTypes[type];
20827     },
20828
20829     /**
20830      * Unsets an invalid handle id
20831      * @method removeInvalidHandleId
20832      * @param {string} id the id of the element to re-enable
20833      */
20834     removeInvalidHandleId: function(id) {
20835         if (typeof id !== "string") {
20836             id = Roo.id(id);
20837         }
20838         delete this.invalidHandleIds[id];
20839     },
20840
20841     /**
20842      * Unsets an invalid css class
20843      * @method removeInvalidHandleClass
20844      * @param {string} cssClass the class of the element(s) you wish to
20845      * re-enable
20846      */
20847     removeInvalidHandleClass: function(cssClass) {
20848         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20849             if (this.invalidHandleClasses[i] == cssClass) {
20850                 delete this.invalidHandleClasses[i];
20851             }
20852         }
20853     },
20854
20855     /**
20856      * Checks the tag exclusion list to see if this click should be ignored
20857      * @method isValidHandleChild
20858      * @param {HTMLElement} node the HTMLElement to evaluate
20859      * @return {boolean} true if this is a valid tag type, false if not
20860      */
20861     isValidHandleChild: function(node) {
20862
20863         var valid = true;
20864         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20865         var nodeName;
20866         try {
20867             nodeName = node.nodeName.toUpperCase();
20868         } catch(e) {
20869             nodeName = node.nodeName;
20870         }
20871         valid = valid && !this.invalidHandleTypes[nodeName];
20872         valid = valid && !this.invalidHandleIds[node.id];
20873
20874         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20875             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20876         }
20877
20878
20879         return valid;
20880
20881     },
20882
20883     /**
20884      * Create the array of horizontal tick marks if an interval was specified
20885      * in setXConstraint().
20886      * @method setXTicks
20887      * @private
20888      */
20889     setXTicks: function(iStartX, iTickSize) {
20890         this.xTicks = [];
20891         this.xTickSize = iTickSize;
20892
20893         var tickMap = {};
20894
20895         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20896             if (!tickMap[i]) {
20897                 this.xTicks[this.xTicks.length] = i;
20898                 tickMap[i] = true;
20899             }
20900         }
20901
20902         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20903             if (!tickMap[i]) {
20904                 this.xTicks[this.xTicks.length] = i;
20905                 tickMap[i] = true;
20906             }
20907         }
20908
20909         this.xTicks.sort(this.DDM.numericSort) ;
20910     },
20911
20912     /**
20913      * Create the array of vertical tick marks if an interval was specified in
20914      * setYConstraint().
20915      * @method setYTicks
20916      * @private
20917      */
20918     setYTicks: function(iStartY, iTickSize) {
20919         this.yTicks = [];
20920         this.yTickSize = iTickSize;
20921
20922         var tickMap = {};
20923
20924         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20925             if (!tickMap[i]) {
20926                 this.yTicks[this.yTicks.length] = i;
20927                 tickMap[i] = true;
20928             }
20929         }
20930
20931         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20932             if (!tickMap[i]) {
20933                 this.yTicks[this.yTicks.length] = i;
20934                 tickMap[i] = true;
20935             }
20936         }
20937
20938         this.yTicks.sort(this.DDM.numericSort) ;
20939     },
20940
20941     /**
20942      * By default, the element can be dragged any place on the screen.  Use
20943      * this method to limit the horizontal travel of the element.  Pass in
20944      * 0,0 for the parameters if you want to lock the drag to the y axis.
20945      * @method setXConstraint
20946      * @param {int} iLeft the number of pixels the element can move to the left
20947      * @param {int} iRight the number of pixels the element can move to the
20948      * right
20949      * @param {int} iTickSize optional parameter for specifying that the
20950      * element
20951      * should move iTickSize pixels at a time.
20952      */
20953     setXConstraint: function(iLeft, iRight, iTickSize) {
20954         this.leftConstraint = iLeft;
20955         this.rightConstraint = iRight;
20956
20957         this.minX = this.initPageX - iLeft;
20958         this.maxX = this.initPageX + iRight;
20959         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20960
20961         this.constrainX = true;
20962     },
20963
20964     /**
20965      * Clears any constraints applied to this instance.  Also clears ticks
20966      * since they can't exist independent of a constraint at this time.
20967      * @method clearConstraints
20968      */
20969     clearConstraints: function() {
20970         this.constrainX = false;
20971         this.constrainY = false;
20972         this.clearTicks();
20973     },
20974
20975     /**
20976      * Clears any tick interval defined for this instance
20977      * @method clearTicks
20978      */
20979     clearTicks: function() {
20980         this.xTicks = null;
20981         this.yTicks = null;
20982         this.xTickSize = 0;
20983         this.yTickSize = 0;
20984     },
20985
20986     /**
20987      * By default, the element can be dragged any place on the screen.  Set
20988      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20989      * parameters if you want to lock the drag to the x axis.
20990      * @method setYConstraint
20991      * @param {int} iUp the number of pixels the element can move up
20992      * @param {int} iDown the number of pixels the element can move down
20993      * @param {int} iTickSize optional parameter for specifying that the
20994      * element should move iTickSize pixels at a time.
20995      */
20996     setYConstraint: function(iUp, iDown, iTickSize) {
20997         this.topConstraint = iUp;
20998         this.bottomConstraint = iDown;
20999
21000         this.minY = this.initPageY - iUp;
21001         this.maxY = this.initPageY + iDown;
21002         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
21003
21004         this.constrainY = true;
21005
21006     },
21007
21008     /**
21009      * resetConstraints must be called if you manually reposition a dd element.
21010      * @method resetConstraints
21011      * @param {boolean} maintainOffset
21012      */
21013     resetConstraints: function() {
21014
21015
21016         // Maintain offsets if necessary
21017         if (this.initPageX || this.initPageX === 0) {
21018             // figure out how much this thing has moved
21019             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
21020             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
21021
21022             this.setInitPosition(dx, dy);
21023
21024         // This is the first time we have detected the element's position
21025         } else {
21026             this.setInitPosition();
21027         }
21028
21029         if (this.constrainX) {
21030             this.setXConstraint( this.leftConstraint,
21031                                  this.rightConstraint,
21032                                  this.xTickSize        );
21033         }
21034
21035         if (this.constrainY) {
21036             this.setYConstraint( this.topConstraint,
21037                                  this.bottomConstraint,
21038                                  this.yTickSize         );
21039         }
21040     },
21041
21042     /**
21043      * Normally the drag element is moved pixel by pixel, but we can specify
21044      * that it move a number of pixels at a time.  This method resolves the
21045      * location when we have it set up like this.
21046      * @method getTick
21047      * @param {int} val where we want to place the object
21048      * @param {int[]} tickArray sorted array of valid points
21049      * @return {int} the closest tick
21050      * @private
21051      */
21052     getTick: function(val, tickArray) {
21053
21054         if (!tickArray) {
21055             // If tick interval is not defined, it is effectively 1 pixel,
21056             // so we return the value passed to us.
21057             return val;
21058         } else if (tickArray[0] >= val) {
21059             // The value is lower than the first tick, so we return the first
21060             // tick.
21061             return tickArray[0];
21062         } else {
21063             for (var i=0, len=tickArray.length; i<len; ++i) {
21064                 var next = i + 1;
21065                 if (tickArray[next] && tickArray[next] >= val) {
21066                     var diff1 = val - tickArray[i];
21067                     var diff2 = tickArray[next] - val;
21068                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
21069                 }
21070             }
21071
21072             // The value is larger than the last tick, so we return the last
21073             // tick.
21074             return tickArray[tickArray.length - 1];
21075         }
21076     },
21077
21078     /**
21079      * toString method
21080      * @method toString
21081      * @return {string} string representation of the dd obj
21082      */
21083     toString: function() {
21084         return ("DragDrop " + this.id);
21085     }
21086
21087 });
21088
21089 })();
21090 /*
21091  * Based on:
21092  * Ext JS Library 1.1.1
21093  * Copyright(c) 2006-2007, Ext JS, LLC.
21094  *
21095  * Originally Released Under LGPL - original licence link has changed is not relivant.
21096  *
21097  * Fork - LGPL
21098  * <script type="text/javascript">
21099  */
21100
21101
21102 /**
21103  * The drag and drop utility provides a framework for building drag and drop
21104  * applications.  In addition to enabling drag and drop for specific elements,
21105  * the drag and drop elements are tracked by the manager class, and the
21106  * interactions between the various elements are tracked during the drag and
21107  * the implementing code is notified about these important moments.
21108  */
21109
21110 // Only load the library once.  Rewriting the manager class would orphan
21111 // existing drag and drop instances.
21112 if (!Roo.dd.DragDropMgr) {
21113
21114 /**
21115  * @class Roo.dd.DragDropMgr
21116  * DragDropMgr is a singleton that tracks the element interaction for
21117  * all DragDrop items in the window.  Generally, you will not call
21118  * this class directly, but it does have helper methods that could
21119  * be useful in your DragDrop implementations.
21120  * @static
21121  */
21122 Roo.dd.DragDropMgr = function() {
21123
21124     var Event = Roo.EventManager;
21125
21126     return {
21127
21128         /**
21129          * Two dimensional Array of registered DragDrop objects.  The first
21130          * dimension is the DragDrop item group, the second the DragDrop
21131          * object.
21132          * @property ids
21133          * @type {string: string}
21134          * @private
21135          * @static
21136          */
21137         ids: {},
21138
21139         /**
21140          * Array of element ids defined as drag handles.  Used to determine
21141          * if the element that generated the mousedown event is actually the
21142          * handle and not the html element itself.
21143          * @property handleIds
21144          * @type {string: string}
21145          * @private
21146          * @static
21147          */
21148         handleIds: {},
21149
21150         /**
21151          * the DragDrop object that is currently being dragged
21152          * @property dragCurrent
21153          * @type DragDrop
21154          * @private
21155          * @static
21156          **/
21157         dragCurrent: null,
21158
21159         /**
21160          * the DragDrop object(s) that are being hovered over
21161          * @property dragOvers
21162          * @type Array
21163          * @private
21164          * @static
21165          */
21166         dragOvers: {},
21167
21168         /**
21169          * the X distance between the cursor and the object being dragged
21170          * @property deltaX
21171          * @type int
21172          * @private
21173          * @static
21174          */
21175         deltaX: 0,
21176
21177         /**
21178          * the Y distance between the cursor and the object being dragged
21179          * @property deltaY
21180          * @type int
21181          * @private
21182          * @static
21183          */
21184         deltaY: 0,
21185
21186         /**
21187          * Flag to determine if we should prevent the default behavior of the
21188          * events we define. By default this is true, but this can be set to
21189          * false if you need the default behavior (not recommended)
21190          * @property preventDefault
21191          * @type boolean
21192          * @static
21193          */
21194         preventDefault: true,
21195
21196         /**
21197          * Flag to determine if we should stop the propagation of the events
21198          * we generate. This is true by default but you may want to set it to
21199          * false if the html element contains other features that require the
21200          * mouse click.
21201          * @property stopPropagation
21202          * @type boolean
21203          * @static
21204          */
21205         stopPropagation: true,
21206
21207         /**
21208          * Internal flag that is set to true when drag and drop has been
21209          * intialized
21210          * @property initialized
21211          * @private
21212          * @static
21213          */
21214         initalized: false,
21215
21216         /**
21217          * All drag and drop can be disabled.
21218          * @property locked
21219          * @private
21220          * @static
21221          */
21222         locked: false,
21223
21224         /**
21225          * Called the first time an element is registered.
21226          * @method init
21227          * @private
21228          * @static
21229          */
21230         init: function() {
21231             this.initialized = true;
21232         },
21233
21234         /**
21235          * In point mode, drag and drop interaction is defined by the
21236          * location of the cursor during the drag/drop
21237          * @property POINT
21238          * @type int
21239          * @static
21240          */
21241         POINT: 0,
21242
21243         /**
21244          * In intersect mode, drag and drop interactio nis defined by the
21245          * overlap of two or more drag and drop objects.
21246          * @property INTERSECT
21247          * @type int
21248          * @static
21249          */
21250         INTERSECT: 1,
21251
21252         /**
21253          * The current drag and drop mode.  Default: POINT
21254          * @property mode
21255          * @type int
21256          * @static
21257          */
21258         mode: 0,
21259
21260         /**
21261          * Runs method on all drag and drop objects
21262          * @method _execOnAll
21263          * @private
21264          * @static
21265          */
21266         _execOnAll: function(sMethod, args) {
21267             for (var i in this.ids) {
21268                 for (var j in this.ids[i]) {
21269                     var oDD = this.ids[i][j];
21270                     if (! this.isTypeOfDD(oDD)) {
21271                         continue;
21272                     }
21273                     oDD[sMethod].apply(oDD, args);
21274                 }
21275             }
21276         },
21277
21278         /**
21279          * Drag and drop initialization.  Sets up the global event handlers
21280          * @method _onLoad
21281          * @private
21282          * @static
21283          */
21284         _onLoad: function() {
21285
21286             this.init();
21287
21288             if (!Roo.isTouch) {
21289                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
21290                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
21291             }
21292             Event.on(document, "touchend",   this.handleMouseUp, this, true);
21293             Event.on(document, "touchmove", this.handleMouseMove, this, true);
21294             
21295             Event.on(window,   "unload",    this._onUnload, this, true);
21296             Event.on(window,   "resize",    this._onResize, this, true);
21297             // Event.on(window,   "mouseout",    this._test);
21298
21299         },
21300
21301         /**
21302          * Reset constraints on all drag and drop objs
21303          * @method _onResize
21304          * @private
21305          * @static
21306          */
21307         _onResize: function(e) {
21308             this._execOnAll("resetConstraints", []);
21309         },
21310
21311         /**
21312          * Lock all drag and drop functionality
21313          * @method lock
21314          * @static
21315          */
21316         lock: function() { this.locked = true; },
21317
21318         /**
21319          * Unlock all drag and drop functionality
21320          * @method unlock
21321          * @static
21322          */
21323         unlock: function() { this.locked = false; },
21324
21325         /**
21326          * Is drag and drop locked?
21327          * @method isLocked
21328          * @return {boolean} True if drag and drop is locked, false otherwise.
21329          * @static
21330          */
21331         isLocked: function() { return this.locked; },
21332
21333         /**
21334          * Location cache that is set for all drag drop objects when a drag is
21335          * initiated, cleared when the drag is finished.
21336          * @property locationCache
21337          * @private
21338          * @static
21339          */
21340         locationCache: {},
21341
21342         /**
21343          * Set useCache to false if you want to force object the lookup of each
21344          * drag and drop linked element constantly during a drag.
21345          * @property useCache
21346          * @type boolean
21347          * @static
21348          */
21349         useCache: true,
21350
21351         /**
21352          * The number of pixels that the mouse needs to move after the
21353          * mousedown before the drag is initiated.  Default=3;
21354          * @property clickPixelThresh
21355          * @type int
21356          * @static
21357          */
21358         clickPixelThresh: 3,
21359
21360         /**
21361          * The number of milliseconds after the mousedown event to initiate the
21362          * drag if we don't get a mouseup event. Default=1000
21363          * @property clickTimeThresh
21364          * @type int
21365          * @static
21366          */
21367         clickTimeThresh: 350,
21368
21369         /**
21370          * Flag that indicates that either the drag pixel threshold or the
21371          * mousdown time threshold has been met
21372          * @property dragThreshMet
21373          * @type boolean
21374          * @private
21375          * @static
21376          */
21377         dragThreshMet: false,
21378
21379         /**
21380          * Timeout used for the click time threshold
21381          * @property clickTimeout
21382          * @type Object
21383          * @private
21384          * @static
21385          */
21386         clickTimeout: null,
21387
21388         /**
21389          * The X position of the mousedown event stored for later use when a
21390          * drag threshold is met.
21391          * @property startX
21392          * @type int
21393          * @private
21394          * @static
21395          */
21396         startX: 0,
21397
21398         /**
21399          * The Y position of the mousedown event stored for later use when a
21400          * drag threshold is met.
21401          * @property startY
21402          * @type int
21403          * @private
21404          * @static
21405          */
21406         startY: 0,
21407
21408         /**
21409          * Each DragDrop instance must be registered with the DragDropMgr.
21410          * This is executed in DragDrop.init()
21411          * @method regDragDrop
21412          * @param {DragDrop} oDD the DragDrop object to register
21413          * @param {String} sGroup the name of the group this element belongs to
21414          * @static
21415          */
21416         regDragDrop: function(oDD, sGroup) {
21417             if (!this.initialized) { this.init(); }
21418
21419             if (!this.ids[sGroup]) {
21420                 this.ids[sGroup] = {};
21421             }
21422             this.ids[sGroup][oDD.id] = oDD;
21423         },
21424
21425         /**
21426          * Removes the supplied dd instance from the supplied group. Executed
21427          * by DragDrop.removeFromGroup, so don't call this function directly.
21428          * @method removeDDFromGroup
21429          * @private
21430          * @static
21431          */
21432         removeDDFromGroup: function(oDD, sGroup) {
21433             if (!this.ids[sGroup]) {
21434                 this.ids[sGroup] = {};
21435             }
21436
21437             var obj = this.ids[sGroup];
21438             if (obj && obj[oDD.id]) {
21439                 delete obj[oDD.id];
21440             }
21441         },
21442
21443         /**
21444          * Unregisters a drag and drop item.  This is executed in
21445          * DragDrop.unreg, use that method instead of calling this directly.
21446          * @method _remove
21447          * @private
21448          * @static
21449          */
21450         _remove: function(oDD) {
21451             for (var g in oDD.groups) {
21452                 if (g && this.ids[g][oDD.id]) {
21453                     delete this.ids[g][oDD.id];
21454                 }
21455             }
21456             delete this.handleIds[oDD.id];
21457         },
21458
21459         /**
21460          * Each DragDrop handle element must be registered.  This is done
21461          * automatically when executing DragDrop.setHandleElId()
21462          * @method regHandle
21463          * @param {String} sDDId the DragDrop id this element is a handle for
21464          * @param {String} sHandleId the id of the element that is the drag
21465          * handle
21466          * @static
21467          */
21468         regHandle: function(sDDId, sHandleId) {
21469             if (!this.handleIds[sDDId]) {
21470                 this.handleIds[sDDId] = {};
21471             }
21472             this.handleIds[sDDId][sHandleId] = sHandleId;
21473         },
21474
21475         /**
21476          * Utility function to determine if a given element has been
21477          * registered as a drag drop item.
21478          * @method isDragDrop
21479          * @param {String} id the element id to check
21480          * @return {boolean} true if this element is a DragDrop item,
21481          * false otherwise
21482          * @static
21483          */
21484         isDragDrop: function(id) {
21485             return ( this.getDDById(id) ) ? true : false;
21486         },
21487
21488         /**
21489          * Returns the drag and drop instances that are in all groups the
21490          * passed in instance belongs to.
21491          * @method getRelated
21492          * @param {DragDrop} p_oDD the obj to get related data for
21493          * @param {boolean} bTargetsOnly if true, only return targetable objs
21494          * @return {DragDrop[]} the related instances
21495          * @static
21496          */
21497         getRelated: function(p_oDD, bTargetsOnly) {
21498             var oDDs = [];
21499             for (var i in p_oDD.groups) {
21500                 for (j in this.ids[i]) {
21501                     var dd = this.ids[i][j];
21502                     if (! this.isTypeOfDD(dd)) {
21503                         continue;
21504                     }
21505                     if (!bTargetsOnly || dd.isTarget) {
21506                         oDDs[oDDs.length] = dd;
21507                     }
21508                 }
21509             }
21510
21511             return oDDs;
21512         },
21513
21514         /**
21515          * Returns true if the specified dd target is a legal target for
21516          * the specifice drag obj
21517          * @method isLegalTarget
21518          * @param {DragDrop} the drag obj
21519          * @param {DragDrop} the target
21520          * @return {boolean} true if the target is a legal target for the
21521          * dd obj
21522          * @static
21523          */
21524         isLegalTarget: function (oDD, oTargetDD) {
21525             var targets = this.getRelated(oDD, true);
21526             for (var i=0, len=targets.length;i<len;++i) {
21527                 if (targets[i].id == oTargetDD.id) {
21528                     return true;
21529                 }
21530             }
21531
21532             return false;
21533         },
21534
21535         /**
21536          * My goal is to be able to transparently determine if an object is
21537          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21538          * returns "object", oDD.constructor.toString() always returns
21539          * "DragDrop" and not the name of the subclass.  So for now it just
21540          * evaluates a well-known variable in DragDrop.
21541          * @method isTypeOfDD
21542          * @param {Object} the object to evaluate
21543          * @return {boolean} true if typeof oDD = DragDrop
21544          * @static
21545          */
21546         isTypeOfDD: function (oDD) {
21547             return (oDD && oDD.__ygDragDrop);
21548         },
21549
21550         /**
21551          * Utility function to determine if a given element has been
21552          * registered as a drag drop handle for the given Drag Drop object.
21553          * @method isHandle
21554          * @param {String} id the element id to check
21555          * @return {boolean} true if this element is a DragDrop handle, false
21556          * otherwise
21557          * @static
21558          */
21559         isHandle: function(sDDId, sHandleId) {
21560             return ( this.handleIds[sDDId] &&
21561                             this.handleIds[sDDId][sHandleId] );
21562         },
21563
21564         /**
21565          * Returns the DragDrop instance for a given id
21566          * @method getDDById
21567          * @param {String} id the id of the DragDrop object
21568          * @return {DragDrop} the drag drop object, null if it is not found
21569          * @static
21570          */
21571         getDDById: function(id) {
21572             for (var i in this.ids) {
21573                 if (this.ids[i][id]) {
21574                     return this.ids[i][id];
21575                 }
21576             }
21577             return null;
21578         },
21579
21580         /**
21581          * Fired after a registered DragDrop object gets the mousedown event.
21582          * Sets up the events required to track the object being dragged
21583          * @method handleMouseDown
21584          * @param {Event} e the event
21585          * @param oDD the DragDrop object being dragged
21586          * @private
21587          * @static
21588          */
21589         handleMouseDown: function(e, oDD) {
21590             if(Roo.QuickTips){
21591                 Roo.QuickTips.disable();
21592             }
21593             this.currentTarget = e.getTarget();
21594
21595             this.dragCurrent = oDD;
21596
21597             var el = oDD.getEl();
21598
21599             // track start position
21600             this.startX = e.getPageX();
21601             this.startY = e.getPageY();
21602
21603             this.deltaX = this.startX - el.offsetLeft;
21604             this.deltaY = this.startY - el.offsetTop;
21605
21606             this.dragThreshMet = false;
21607
21608             this.clickTimeout = setTimeout(
21609                     function() {
21610                         var DDM = Roo.dd.DDM;
21611                         DDM.startDrag(DDM.startX, DDM.startY);
21612                     },
21613                     this.clickTimeThresh );
21614         },
21615
21616         /**
21617          * Fired when either the drag pixel threshol or the mousedown hold
21618          * time threshold has been met.
21619          * @method startDrag
21620          * @param x {int} the X position of the original mousedown
21621          * @param y {int} the Y position of the original mousedown
21622          * @static
21623          */
21624         startDrag: function(x, y) {
21625             clearTimeout(this.clickTimeout);
21626             if (this.dragCurrent) {
21627                 this.dragCurrent.b4StartDrag(x, y);
21628                 this.dragCurrent.startDrag(x, y);
21629             }
21630             this.dragThreshMet = true;
21631         },
21632
21633         /**
21634          * Internal function to handle the mouseup event.  Will be invoked
21635          * from the context of the document.
21636          * @method handleMouseUp
21637          * @param {Event} e the event
21638          * @private
21639          * @static
21640          */
21641         handleMouseUp: function(e) {
21642
21643             if(Roo.QuickTips){
21644                 Roo.QuickTips.enable();
21645             }
21646             if (! this.dragCurrent) {
21647                 return;
21648             }
21649
21650             clearTimeout(this.clickTimeout);
21651
21652             if (this.dragThreshMet) {
21653                 this.fireEvents(e, true);
21654             } else {
21655             }
21656
21657             this.stopDrag(e);
21658
21659             this.stopEvent(e);
21660         },
21661
21662         /**
21663          * Utility to stop event propagation and event default, if these
21664          * features are turned on.
21665          * @method stopEvent
21666          * @param {Event} e the event as returned by this.getEvent()
21667          * @static
21668          */
21669         stopEvent: function(e){
21670             if(this.stopPropagation) {
21671                 e.stopPropagation();
21672             }
21673
21674             if (this.preventDefault) {
21675                 e.preventDefault();
21676             }
21677         },
21678
21679         /**
21680          * Internal function to clean up event handlers after the drag
21681          * operation is complete
21682          * @method stopDrag
21683          * @param {Event} e the event
21684          * @private
21685          * @static
21686          */
21687         stopDrag: function(e) {
21688             // Fire the drag end event for the item that was dragged
21689             if (this.dragCurrent) {
21690                 if (this.dragThreshMet) {
21691                     this.dragCurrent.b4EndDrag(e);
21692                     this.dragCurrent.endDrag(e);
21693                 }
21694
21695                 this.dragCurrent.onMouseUp(e);
21696             }
21697
21698             this.dragCurrent = null;
21699             this.dragOvers = {};
21700         },
21701
21702         /**
21703          * Internal function to handle the mousemove event.  Will be invoked
21704          * from the context of the html element.
21705          *
21706          * @TODO figure out what we can do about mouse events lost when the
21707          * user drags objects beyond the window boundary.  Currently we can
21708          * detect this in internet explorer by verifying that the mouse is
21709          * down during the mousemove event.  Firefox doesn't give us the
21710          * button state on the mousemove event.
21711          * @method handleMouseMove
21712          * @param {Event} e the event
21713          * @private
21714          * @static
21715          */
21716         handleMouseMove: function(e) {
21717             if (! this.dragCurrent) {
21718                 return true;
21719             }
21720
21721             // var button = e.which || e.button;
21722
21723             // check for IE mouseup outside of page boundary
21724             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21725                 this.stopEvent(e);
21726                 return this.handleMouseUp(e);
21727             }
21728
21729             if (!this.dragThreshMet) {
21730                 var diffX = Math.abs(this.startX - e.getPageX());
21731                 var diffY = Math.abs(this.startY - e.getPageY());
21732                 if (diffX > this.clickPixelThresh ||
21733                             diffY > this.clickPixelThresh) {
21734                     this.startDrag(this.startX, this.startY);
21735                 }
21736             }
21737
21738             if (this.dragThreshMet) {
21739                 this.dragCurrent.b4Drag(e);
21740                 this.dragCurrent.onDrag(e);
21741                 if(!this.dragCurrent.moveOnly){
21742                     this.fireEvents(e, false);
21743                 }
21744             }
21745
21746             this.stopEvent(e);
21747
21748             return true;
21749         },
21750
21751         /**
21752          * Iterates over all of the DragDrop elements to find ones we are
21753          * hovering over or dropping on
21754          * @method fireEvents
21755          * @param {Event} e the event
21756          * @param {boolean} isDrop is this a drop op or a mouseover op?
21757          * @private
21758          * @static
21759          */
21760         fireEvents: function(e, isDrop) {
21761             var dc = this.dragCurrent;
21762
21763             // If the user did the mouse up outside of the window, we could
21764             // get here even though we have ended the drag.
21765             if (!dc || dc.isLocked()) {
21766                 return;
21767             }
21768
21769             var pt = e.getPoint();
21770
21771             // cache the previous dragOver array
21772             var oldOvers = [];
21773
21774             var outEvts   = [];
21775             var overEvts  = [];
21776             var dropEvts  = [];
21777             var enterEvts = [];
21778
21779             // Check to see if the object(s) we were hovering over is no longer
21780             // being hovered over so we can fire the onDragOut event
21781             for (var i in this.dragOvers) {
21782
21783                 var ddo = this.dragOvers[i];
21784
21785                 if (! this.isTypeOfDD(ddo)) {
21786                     continue;
21787                 }
21788
21789                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21790                     outEvts.push( ddo );
21791                 }
21792
21793                 oldOvers[i] = true;
21794                 delete this.dragOvers[i];
21795             }
21796
21797             for (var sGroup in dc.groups) {
21798
21799                 if ("string" != typeof sGroup) {
21800                     continue;
21801                 }
21802
21803                 for (i in this.ids[sGroup]) {
21804                     var oDD = this.ids[sGroup][i];
21805                     if (! this.isTypeOfDD(oDD)) {
21806                         continue;
21807                     }
21808
21809                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21810                         if (this.isOverTarget(pt, oDD, this.mode)) {
21811                             // look for drop interactions
21812                             if (isDrop) {
21813                                 dropEvts.push( oDD );
21814                             // look for drag enter and drag over interactions
21815                             } else {
21816
21817                                 // initial drag over: dragEnter fires
21818                                 if (!oldOvers[oDD.id]) {
21819                                     enterEvts.push( oDD );
21820                                 // subsequent drag overs: dragOver fires
21821                                 } else {
21822                                     overEvts.push( oDD );
21823                                 }
21824
21825                                 this.dragOvers[oDD.id] = oDD;
21826                             }
21827                         }
21828                     }
21829                 }
21830             }
21831
21832             if (this.mode) {
21833                 if (outEvts.length) {
21834                     dc.b4DragOut(e, outEvts);
21835                     dc.onDragOut(e, outEvts);
21836                 }
21837
21838                 if (enterEvts.length) {
21839                     dc.onDragEnter(e, enterEvts);
21840                 }
21841
21842                 if (overEvts.length) {
21843                     dc.b4DragOver(e, overEvts);
21844                     dc.onDragOver(e, overEvts);
21845                 }
21846
21847                 if (dropEvts.length) {
21848                     dc.b4DragDrop(e, dropEvts);
21849                     dc.onDragDrop(e, dropEvts);
21850                 }
21851
21852             } else {
21853                 // fire dragout events
21854                 var len = 0;
21855                 for (i=0, len=outEvts.length; i<len; ++i) {
21856                     dc.b4DragOut(e, outEvts[i].id);
21857                     dc.onDragOut(e, outEvts[i].id);
21858                 }
21859
21860                 // fire enter events
21861                 for (i=0,len=enterEvts.length; i<len; ++i) {
21862                     // dc.b4DragEnter(e, oDD.id);
21863                     dc.onDragEnter(e, enterEvts[i].id);
21864                 }
21865
21866                 // fire over events
21867                 for (i=0,len=overEvts.length; i<len; ++i) {
21868                     dc.b4DragOver(e, overEvts[i].id);
21869                     dc.onDragOver(e, overEvts[i].id);
21870                 }
21871
21872                 // fire drop events
21873                 for (i=0, len=dropEvts.length; i<len; ++i) {
21874                     dc.b4DragDrop(e, dropEvts[i].id);
21875                     dc.onDragDrop(e, dropEvts[i].id);
21876                 }
21877
21878             }
21879
21880             // notify about a drop that did not find a target
21881             if (isDrop && !dropEvts.length) {
21882                 dc.onInvalidDrop(e);
21883             }
21884
21885         },
21886
21887         /**
21888          * Helper function for getting the best match from the list of drag
21889          * and drop objects returned by the drag and drop events when we are
21890          * in INTERSECT mode.  It returns either the first object that the
21891          * cursor is over, or the object that has the greatest overlap with
21892          * the dragged element.
21893          * @method getBestMatch
21894          * @param  {DragDrop[]} dds The array of drag and drop objects
21895          * targeted
21896          * @return {DragDrop}       The best single match
21897          * @static
21898          */
21899         getBestMatch: function(dds) {
21900             var winner = null;
21901             // Return null if the input is not what we expect
21902             //if (!dds || !dds.length || dds.length == 0) {
21903                // winner = null;
21904             // If there is only one item, it wins
21905             //} else if (dds.length == 1) {
21906
21907             var len = dds.length;
21908
21909             if (len == 1) {
21910                 winner = dds[0];
21911             } else {
21912                 // Loop through the targeted items
21913                 for (var i=0; i<len; ++i) {
21914                     var dd = dds[i];
21915                     // If the cursor is over the object, it wins.  If the
21916                     // cursor is over multiple matches, the first one we come
21917                     // to wins.
21918                     if (dd.cursorIsOver) {
21919                         winner = dd;
21920                         break;
21921                     // Otherwise the object with the most overlap wins
21922                     } else {
21923                         if (!winner ||
21924                             winner.overlap.getArea() < dd.overlap.getArea()) {
21925                             winner = dd;
21926                         }
21927                     }
21928                 }
21929             }
21930
21931             return winner;
21932         },
21933
21934         /**
21935          * Refreshes the cache of the top-left and bottom-right points of the
21936          * drag and drop objects in the specified group(s).  This is in the
21937          * format that is stored in the drag and drop instance, so typical
21938          * usage is:
21939          * <code>
21940          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21941          * </code>
21942          * Alternatively:
21943          * <code>
21944          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21945          * </code>
21946          * @TODO this really should be an indexed array.  Alternatively this
21947          * method could accept both.
21948          * @method refreshCache
21949          * @param {Object} groups an associative array of groups to refresh
21950          * @static
21951          */
21952         refreshCache: function(groups) {
21953             for (var sGroup in groups) {
21954                 if ("string" != typeof sGroup) {
21955                     continue;
21956                 }
21957                 for (var i in this.ids[sGroup]) {
21958                     var oDD = this.ids[sGroup][i];
21959
21960                     if (this.isTypeOfDD(oDD)) {
21961                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21962                         var loc = this.getLocation(oDD);
21963                         if (loc) {
21964                             this.locationCache[oDD.id] = loc;
21965                         } else {
21966                             delete this.locationCache[oDD.id];
21967                             // this will unregister the drag and drop object if
21968                             // the element is not in a usable state
21969                             // oDD.unreg();
21970                         }
21971                     }
21972                 }
21973             }
21974         },
21975
21976         /**
21977          * This checks to make sure an element exists and is in the DOM.  The
21978          * main purpose is to handle cases where innerHTML is used to remove
21979          * drag and drop objects from the DOM.  IE provides an 'unspecified
21980          * error' when trying to access the offsetParent of such an element
21981          * @method verifyEl
21982          * @param {HTMLElement} el the element to check
21983          * @return {boolean} true if the element looks usable
21984          * @static
21985          */
21986         verifyEl: function(el) {
21987             if (el) {
21988                 var parent;
21989                 if(Roo.isIE){
21990                     try{
21991                         parent = el.offsetParent;
21992                     }catch(e){}
21993                 }else{
21994                     parent = el.offsetParent;
21995                 }
21996                 if (parent) {
21997                     return true;
21998                 }
21999             }
22000
22001             return false;
22002         },
22003
22004         /**
22005          * Returns a Region object containing the drag and drop element's position
22006          * and size, including the padding configured for it
22007          * @method getLocation
22008          * @param {DragDrop} oDD the drag and drop object to get the
22009          *                       location for
22010          * @return {Roo.lib.Region} a Region object representing the total area
22011          *                             the element occupies, including any padding
22012          *                             the instance is configured for.
22013          * @static
22014          */
22015         getLocation: function(oDD) {
22016             if (! this.isTypeOfDD(oDD)) {
22017                 return null;
22018             }
22019
22020             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
22021
22022             try {
22023                 pos= Roo.lib.Dom.getXY(el);
22024             } catch (e) { }
22025
22026             if (!pos) {
22027                 return null;
22028             }
22029
22030             x1 = pos[0];
22031             x2 = x1 + el.offsetWidth;
22032             y1 = pos[1];
22033             y2 = y1 + el.offsetHeight;
22034
22035             t = y1 - oDD.padding[0];
22036             r = x2 + oDD.padding[1];
22037             b = y2 + oDD.padding[2];
22038             l = x1 - oDD.padding[3];
22039
22040             return new Roo.lib.Region( t, r, b, l );
22041         },
22042
22043         /**
22044          * Checks the cursor location to see if it over the target
22045          * @method isOverTarget
22046          * @param {Roo.lib.Point} pt The point to evaluate
22047          * @param {DragDrop} oTarget the DragDrop object we are inspecting
22048          * @return {boolean} true if the mouse is over the target
22049          * @private
22050          * @static
22051          */
22052         isOverTarget: function(pt, oTarget, intersect) {
22053             // use cache if available
22054             var loc = this.locationCache[oTarget.id];
22055             if (!loc || !this.useCache) {
22056                 loc = this.getLocation(oTarget);
22057                 this.locationCache[oTarget.id] = loc;
22058
22059             }
22060
22061             if (!loc) {
22062                 return false;
22063             }
22064
22065             oTarget.cursorIsOver = loc.contains( pt );
22066
22067             // DragDrop is using this as a sanity check for the initial mousedown
22068             // in this case we are done.  In POINT mode, if the drag obj has no
22069             // contraints, we are also done. Otherwise we need to evaluate the
22070             // location of the target as related to the actual location of the
22071             // dragged element.
22072             var dc = this.dragCurrent;
22073             if (!dc || !dc.getTargetCoord ||
22074                     (!intersect && !dc.constrainX && !dc.constrainY)) {
22075                 return oTarget.cursorIsOver;
22076             }
22077
22078             oTarget.overlap = null;
22079
22080             // Get the current location of the drag element, this is the
22081             // location of the mouse event less the delta that represents
22082             // where the original mousedown happened on the element.  We
22083             // need to consider constraints and ticks as well.
22084             var pos = dc.getTargetCoord(pt.x, pt.y);
22085
22086             var el = dc.getDragEl();
22087             var curRegion = new Roo.lib.Region( pos.y,
22088                                                    pos.x + el.offsetWidth,
22089                                                    pos.y + el.offsetHeight,
22090                                                    pos.x );
22091
22092             var overlap = curRegion.intersect(loc);
22093
22094             if (overlap) {
22095                 oTarget.overlap = overlap;
22096                 return (intersect) ? true : oTarget.cursorIsOver;
22097             } else {
22098                 return false;
22099             }
22100         },
22101
22102         /**
22103          * unload event handler
22104          * @method _onUnload
22105          * @private
22106          * @static
22107          */
22108         _onUnload: function(e, me) {
22109             Roo.dd.DragDropMgr.unregAll();
22110         },
22111
22112         /**
22113          * Cleans up the drag and drop events and objects.
22114          * @method unregAll
22115          * @private
22116          * @static
22117          */
22118         unregAll: function() {
22119
22120             if (this.dragCurrent) {
22121                 this.stopDrag();
22122                 this.dragCurrent = null;
22123             }
22124
22125             this._execOnAll("unreg", []);
22126
22127             for (i in this.elementCache) {
22128                 delete this.elementCache[i];
22129             }
22130
22131             this.elementCache = {};
22132             this.ids = {};
22133         },
22134
22135         /**
22136          * A cache of DOM elements
22137          * @property elementCache
22138          * @private
22139          * @static
22140          */
22141         elementCache: {},
22142
22143         /**
22144          * Get the wrapper for the DOM element specified
22145          * @method getElWrapper
22146          * @param {String} id the id of the element to get
22147          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
22148          * @private
22149          * @deprecated This wrapper isn't that useful
22150          * @static
22151          */
22152         getElWrapper: function(id) {
22153             var oWrapper = this.elementCache[id];
22154             if (!oWrapper || !oWrapper.el) {
22155                 oWrapper = this.elementCache[id] =
22156                     new this.ElementWrapper(Roo.getDom(id));
22157             }
22158             return oWrapper;
22159         },
22160
22161         /**
22162          * Returns the actual DOM element
22163          * @method getElement
22164          * @param {String} id the id of the elment to get
22165          * @return {Object} The element
22166          * @deprecated use Roo.getDom instead
22167          * @static
22168          */
22169         getElement: function(id) {
22170             return Roo.getDom(id);
22171         },
22172
22173         /**
22174          * Returns the style property for the DOM element (i.e.,
22175          * document.getElById(id).style)
22176          * @method getCss
22177          * @param {String} id the id of the elment to get
22178          * @return {Object} The style property of the element
22179          * @deprecated use Roo.getDom instead
22180          * @static
22181          */
22182         getCss: function(id) {
22183             var el = Roo.getDom(id);
22184             return (el) ? el.style : null;
22185         },
22186
22187         /**
22188          * Inner class for cached elements
22189          * @class DragDropMgr.ElementWrapper
22190          * @for DragDropMgr
22191          * @private
22192          * @deprecated
22193          */
22194         ElementWrapper: function(el) {
22195                 /**
22196                  * The element
22197                  * @property el
22198                  */
22199                 this.el = el || null;
22200                 /**
22201                  * The element id
22202                  * @property id
22203                  */
22204                 this.id = this.el && el.id;
22205                 /**
22206                  * A reference to the style property
22207                  * @property css
22208                  */
22209                 this.css = this.el && el.style;
22210             },
22211
22212         /**
22213          * Returns the X position of an html element
22214          * @method getPosX
22215          * @param el the element for which to get the position
22216          * @return {int} the X coordinate
22217          * @for DragDropMgr
22218          * @deprecated use Roo.lib.Dom.getX instead
22219          * @static
22220          */
22221         getPosX: function(el) {
22222             return Roo.lib.Dom.getX(el);
22223         },
22224
22225         /**
22226          * Returns the Y position of an html element
22227          * @method getPosY
22228          * @param el the element for which to get the position
22229          * @return {int} the Y coordinate
22230          * @deprecated use Roo.lib.Dom.getY instead
22231          * @static
22232          */
22233         getPosY: function(el) {
22234             return Roo.lib.Dom.getY(el);
22235         },
22236
22237         /**
22238          * Swap two nodes.  In IE, we use the native method, for others we
22239          * emulate the IE behavior
22240          * @method swapNode
22241          * @param n1 the first node to swap
22242          * @param n2 the other node to swap
22243          * @static
22244          */
22245         swapNode: function(n1, n2) {
22246             if (n1.swapNode) {
22247                 n1.swapNode(n2);
22248             } else {
22249                 var p = n2.parentNode;
22250                 var s = n2.nextSibling;
22251
22252                 if (s == n1) {
22253                     p.insertBefore(n1, n2);
22254                 } else if (n2 == n1.nextSibling) {
22255                     p.insertBefore(n2, n1);
22256                 } else {
22257                     n1.parentNode.replaceChild(n2, n1);
22258                     p.insertBefore(n1, s);
22259                 }
22260             }
22261         },
22262
22263         /**
22264          * Returns the current scroll position
22265          * @method getScroll
22266          * @private
22267          * @static
22268          */
22269         getScroll: function () {
22270             var t, l, dde=document.documentElement, db=document.body;
22271             if (dde && (dde.scrollTop || dde.scrollLeft)) {
22272                 t = dde.scrollTop;
22273                 l = dde.scrollLeft;
22274             } else if (db) {
22275                 t = db.scrollTop;
22276                 l = db.scrollLeft;
22277             } else {
22278
22279             }
22280             return { top: t, left: l };
22281         },
22282
22283         /**
22284          * Returns the specified element style property
22285          * @method getStyle
22286          * @param {HTMLElement} el          the element
22287          * @param {string}      styleProp   the style property
22288          * @return {string} The value of the style property
22289          * @deprecated use Roo.lib.Dom.getStyle
22290          * @static
22291          */
22292         getStyle: function(el, styleProp) {
22293             return Roo.fly(el).getStyle(styleProp);
22294         },
22295
22296         /**
22297          * Gets the scrollTop
22298          * @method getScrollTop
22299          * @return {int} the document's scrollTop
22300          * @static
22301          */
22302         getScrollTop: function () { return this.getScroll().top; },
22303
22304         /**
22305          * Gets the scrollLeft
22306          * @method getScrollLeft
22307          * @return {int} the document's scrollTop
22308          * @static
22309          */
22310         getScrollLeft: function () { return this.getScroll().left; },
22311
22312         /**
22313          * Sets the x/y position of an element to the location of the
22314          * target element.
22315          * @method moveToEl
22316          * @param {HTMLElement} moveEl      The element to move
22317          * @param {HTMLElement} targetEl    The position reference element
22318          * @static
22319          */
22320         moveToEl: function (moveEl, targetEl) {
22321             var aCoord = Roo.lib.Dom.getXY(targetEl);
22322             Roo.lib.Dom.setXY(moveEl, aCoord);
22323         },
22324
22325         /**
22326          * Numeric array sort function
22327          * @method numericSort
22328          * @static
22329          */
22330         numericSort: function(a, b) { return (a - b); },
22331
22332         /**
22333          * Internal counter
22334          * @property _timeoutCount
22335          * @private
22336          * @static
22337          */
22338         _timeoutCount: 0,
22339
22340         /**
22341          * Trying to make the load order less important.  Without this we get
22342          * an error if this file is loaded before the Event Utility.
22343          * @method _addListeners
22344          * @private
22345          * @static
22346          */
22347         _addListeners: function() {
22348             var DDM = Roo.dd.DDM;
22349             if ( Roo.lib.Event && document ) {
22350                 DDM._onLoad();
22351             } else {
22352                 if (DDM._timeoutCount > 2000) {
22353                 } else {
22354                     setTimeout(DDM._addListeners, 10);
22355                     if (document && document.body) {
22356                         DDM._timeoutCount += 1;
22357                     }
22358                 }
22359             }
22360         },
22361
22362         /**
22363          * Recursively searches the immediate parent and all child nodes for
22364          * the handle element in order to determine wheter or not it was
22365          * clicked.
22366          * @method handleWasClicked
22367          * @param node the html element to inspect
22368          * @static
22369          */
22370         handleWasClicked: function(node, id) {
22371             if (this.isHandle(id, node.id)) {
22372                 return true;
22373             } else {
22374                 // check to see if this is a text node child of the one we want
22375                 var p = node.parentNode;
22376
22377                 while (p) {
22378                     if (this.isHandle(id, p.id)) {
22379                         return true;
22380                     } else {
22381                         p = p.parentNode;
22382                     }
22383                 }
22384             }
22385
22386             return false;
22387         }
22388
22389     };
22390
22391 }();
22392
22393 // shorter alias, save a few bytes
22394 Roo.dd.DDM = Roo.dd.DragDropMgr;
22395 Roo.dd.DDM._addListeners();
22396
22397 }/*
22398  * Based on:
22399  * Ext JS Library 1.1.1
22400  * Copyright(c) 2006-2007, Ext JS, LLC.
22401  *
22402  * Originally Released Under LGPL - original licence link has changed is not relivant.
22403  *
22404  * Fork - LGPL
22405  * <script type="text/javascript">
22406  */
22407
22408 /**
22409  * @class Roo.dd.DD
22410  * A DragDrop implementation where the linked element follows the
22411  * mouse cursor during a drag.
22412  * @extends Roo.dd.DragDrop
22413  * @constructor
22414  * @param {String} id the id of the linked element
22415  * @param {String} sGroup the group of related DragDrop items
22416  * @param {object} config an object containing configurable attributes
22417  *                Valid properties for DD:
22418  *                    scroll
22419  */
22420 Roo.dd.DD = function(id, sGroup, config) {
22421     if (id) {
22422         this.init(id, sGroup, config);
22423     }
22424 };
22425
22426 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22427
22428     /**
22429      * When set to true, the utility automatically tries to scroll the browser
22430      * window wehn a drag and drop element is dragged near the viewport boundary.
22431      * Defaults to true.
22432      * @property scroll
22433      * @type boolean
22434      */
22435     scroll: true,
22436
22437     /**
22438      * Sets the pointer offset to the distance between the linked element's top
22439      * left corner and the location the element was clicked
22440      * @method autoOffset
22441      * @param {int} iPageX the X coordinate of the click
22442      * @param {int} iPageY the Y coordinate of the click
22443      */
22444     autoOffset: function(iPageX, iPageY) {
22445         var x = iPageX - this.startPageX;
22446         var y = iPageY - this.startPageY;
22447         this.setDelta(x, y);
22448     },
22449
22450     /**
22451      * Sets the pointer offset.  You can call this directly to force the
22452      * offset to be in a particular location (e.g., pass in 0,0 to set it
22453      * to the center of the object)
22454      * @method setDelta
22455      * @param {int} iDeltaX the distance from the left
22456      * @param {int} iDeltaY the distance from the top
22457      */
22458     setDelta: function(iDeltaX, iDeltaY) {
22459         this.deltaX = iDeltaX;
22460         this.deltaY = iDeltaY;
22461     },
22462
22463     /**
22464      * Sets the drag element to the location of the mousedown or click event,
22465      * maintaining the cursor location relative to the location on the element
22466      * that was clicked.  Override this if you want to place the element in a
22467      * location other than where the cursor is.
22468      * @method setDragElPos
22469      * @param {int} iPageX the X coordinate of the mousedown or drag event
22470      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22471      */
22472     setDragElPos: function(iPageX, iPageY) {
22473         // the first time we do this, we are going to check to make sure
22474         // the element has css positioning
22475
22476         var el = this.getDragEl();
22477         this.alignElWithMouse(el, iPageX, iPageY);
22478     },
22479
22480     /**
22481      * Sets the element to the location of the mousedown or click event,
22482      * maintaining the cursor location relative to the location on the element
22483      * that was clicked.  Override this if you want to place the element in a
22484      * location other than where the cursor is.
22485      * @method alignElWithMouse
22486      * @param {HTMLElement} el the element to move
22487      * @param {int} iPageX the X coordinate of the mousedown or drag event
22488      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22489      */
22490     alignElWithMouse: function(el, iPageX, iPageY) {
22491         var oCoord = this.getTargetCoord(iPageX, iPageY);
22492         var fly = el.dom ? el : Roo.fly(el);
22493         if (!this.deltaSetXY) {
22494             var aCoord = [oCoord.x, oCoord.y];
22495             fly.setXY(aCoord);
22496             var newLeft = fly.getLeft(true);
22497             var newTop  = fly.getTop(true);
22498             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22499         } else {
22500             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22501         }
22502
22503         this.cachePosition(oCoord.x, oCoord.y);
22504         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22505         return oCoord;
22506     },
22507
22508     /**
22509      * Saves the most recent position so that we can reset the constraints and
22510      * tick marks on-demand.  We need to know this so that we can calculate the
22511      * number of pixels the element is offset from its original position.
22512      * @method cachePosition
22513      * @param iPageX the current x position (optional, this just makes it so we
22514      * don't have to look it up again)
22515      * @param iPageY the current y position (optional, this just makes it so we
22516      * don't have to look it up again)
22517      */
22518     cachePosition: function(iPageX, iPageY) {
22519         if (iPageX) {
22520             this.lastPageX = iPageX;
22521             this.lastPageY = iPageY;
22522         } else {
22523             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22524             this.lastPageX = aCoord[0];
22525             this.lastPageY = aCoord[1];
22526         }
22527     },
22528
22529     /**
22530      * Auto-scroll the window if the dragged object has been moved beyond the
22531      * visible window boundary.
22532      * @method autoScroll
22533      * @param {int} x the drag element's x position
22534      * @param {int} y the drag element's y position
22535      * @param {int} h the height of the drag element
22536      * @param {int} w the width of the drag element
22537      * @private
22538      */
22539     autoScroll: function(x, y, h, w) {
22540
22541         if (this.scroll) {
22542             // The client height
22543             var clientH = Roo.lib.Dom.getViewWidth();
22544
22545             // The client width
22546             var clientW = Roo.lib.Dom.getViewHeight();
22547
22548             // The amt scrolled down
22549             var st = this.DDM.getScrollTop();
22550
22551             // The amt scrolled right
22552             var sl = this.DDM.getScrollLeft();
22553
22554             // Location of the bottom of the element
22555             var bot = h + y;
22556
22557             // Location of the right of the element
22558             var right = w + x;
22559
22560             // The distance from the cursor to the bottom of the visible area,
22561             // adjusted so that we don't scroll if the cursor is beyond the
22562             // element drag constraints
22563             var toBot = (clientH + st - y - this.deltaY);
22564
22565             // The distance from the cursor to the right of the visible area
22566             var toRight = (clientW + sl - x - this.deltaX);
22567
22568
22569             // How close to the edge the cursor must be before we scroll
22570             // var thresh = (document.all) ? 100 : 40;
22571             var thresh = 40;
22572
22573             // How many pixels to scroll per autoscroll op.  This helps to reduce
22574             // clunky scrolling. IE is more sensitive about this ... it needs this
22575             // value to be higher.
22576             var scrAmt = (document.all) ? 80 : 30;
22577
22578             // Scroll down if we are near the bottom of the visible page and the
22579             // obj extends below the crease
22580             if ( bot > clientH && toBot < thresh ) {
22581                 window.scrollTo(sl, st + scrAmt);
22582             }
22583
22584             // Scroll up if the window is scrolled down and the top of the object
22585             // goes above the top border
22586             if ( y < st && st > 0 && y - st < thresh ) {
22587                 window.scrollTo(sl, st - scrAmt);
22588             }
22589
22590             // Scroll right if the obj is beyond the right border and the cursor is
22591             // near the border.
22592             if ( right > clientW && toRight < thresh ) {
22593                 window.scrollTo(sl + scrAmt, st);
22594             }
22595
22596             // Scroll left if the window has been scrolled to the right and the obj
22597             // extends past the left border
22598             if ( x < sl && sl > 0 && x - sl < thresh ) {
22599                 window.scrollTo(sl - scrAmt, st);
22600             }
22601         }
22602     },
22603
22604     /**
22605      * Finds the location the element should be placed if we want to move
22606      * it to where the mouse location less the click offset would place us.
22607      * @method getTargetCoord
22608      * @param {int} iPageX the X coordinate of the click
22609      * @param {int} iPageY the Y coordinate of the click
22610      * @return an object that contains the coordinates (Object.x and Object.y)
22611      * @private
22612      */
22613     getTargetCoord: function(iPageX, iPageY) {
22614
22615
22616         var x = iPageX - this.deltaX;
22617         var y = iPageY - this.deltaY;
22618
22619         if (this.constrainX) {
22620             if (x < this.minX) { x = this.minX; }
22621             if (x > this.maxX) { x = this.maxX; }
22622         }
22623
22624         if (this.constrainY) {
22625             if (y < this.minY) { y = this.minY; }
22626             if (y > this.maxY) { y = this.maxY; }
22627         }
22628
22629         x = this.getTick(x, this.xTicks);
22630         y = this.getTick(y, this.yTicks);
22631
22632
22633         return {x:x, y:y};
22634     },
22635
22636     /*
22637      * Sets up config options specific to this class. Overrides
22638      * Roo.dd.DragDrop, but all versions of this method through the
22639      * inheritance chain are called
22640      */
22641     applyConfig: function() {
22642         Roo.dd.DD.superclass.applyConfig.call(this);
22643         this.scroll = (this.config.scroll !== false);
22644     },
22645
22646     /*
22647      * Event that fires prior to the onMouseDown event.  Overrides
22648      * Roo.dd.DragDrop.
22649      */
22650     b4MouseDown: function(e) {
22651         // this.resetConstraints();
22652         this.autoOffset(e.getPageX(),
22653                             e.getPageY());
22654     },
22655
22656     /*
22657      * Event that fires prior to the onDrag event.  Overrides
22658      * Roo.dd.DragDrop.
22659      */
22660     b4Drag: function(e) {
22661         this.setDragElPos(e.getPageX(),
22662                             e.getPageY());
22663     },
22664
22665     toString: function() {
22666         return ("DD " + this.id);
22667     }
22668
22669     //////////////////////////////////////////////////////////////////////////
22670     // Debugging ygDragDrop events that can be overridden
22671     //////////////////////////////////////////////////////////////////////////
22672     /*
22673     startDrag: function(x, y) {
22674     },
22675
22676     onDrag: function(e) {
22677     },
22678
22679     onDragEnter: function(e, id) {
22680     },
22681
22682     onDragOver: function(e, id) {
22683     },
22684
22685     onDragOut: function(e, id) {
22686     },
22687
22688     onDragDrop: function(e, id) {
22689     },
22690
22691     endDrag: function(e) {
22692     }
22693
22694     */
22695
22696 });/*
22697  * Based on:
22698  * Ext JS Library 1.1.1
22699  * Copyright(c) 2006-2007, Ext JS, LLC.
22700  *
22701  * Originally Released Under LGPL - original licence link has changed is not relivant.
22702  *
22703  * Fork - LGPL
22704  * <script type="text/javascript">
22705  */
22706
22707 /**
22708  * @class Roo.dd.DDProxy
22709  * A DragDrop implementation that inserts an empty, bordered div into
22710  * the document that follows the cursor during drag operations.  At the time of
22711  * the click, the frame div is resized to the dimensions of the linked html
22712  * element, and moved to the exact location of the linked element.
22713  *
22714  * References to the "frame" element refer to the single proxy element that
22715  * was created to be dragged in place of all DDProxy elements on the
22716  * page.
22717  *
22718  * @extends Roo.dd.DD
22719  * @constructor
22720  * @param {String} id the id of the linked html element
22721  * @param {String} sGroup the group of related DragDrop objects
22722  * @param {object} config an object containing configurable attributes
22723  *                Valid properties for DDProxy in addition to those in DragDrop:
22724  *                   resizeFrame, centerFrame, dragElId
22725  */
22726 Roo.dd.DDProxy = function(id, sGroup, config) {
22727     if (id) {
22728         this.init(id, sGroup, config);
22729         this.initFrame();
22730     }
22731 };
22732
22733 /**
22734  * The default drag frame div id
22735  * @property Roo.dd.DDProxy.dragElId
22736  * @type String
22737  * @static
22738  */
22739 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22740
22741 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22742
22743     /**
22744      * By default we resize the drag frame to be the same size as the element
22745      * we want to drag (this is to get the frame effect).  We can turn it off
22746      * if we want a different behavior.
22747      * @property resizeFrame
22748      * @type boolean
22749      */
22750     resizeFrame: true,
22751
22752     /**
22753      * By default the frame is positioned exactly where the drag element is, so
22754      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22755      * you do not have constraints on the obj is to have the drag frame centered
22756      * around the cursor.  Set centerFrame to true for this effect.
22757      * @property centerFrame
22758      * @type boolean
22759      */
22760     centerFrame: false,
22761
22762     /**
22763      * Creates the proxy element if it does not yet exist
22764      * @method createFrame
22765      */
22766     createFrame: function() {
22767         var self = this;
22768         var body = document.body;
22769
22770         if (!body || !body.firstChild) {
22771             setTimeout( function() { self.createFrame(); }, 50 );
22772             return;
22773         }
22774
22775         var div = this.getDragEl();
22776
22777         if (!div) {
22778             div    = document.createElement("div");
22779             div.id = this.dragElId;
22780             var s  = div.style;
22781
22782             s.position   = "absolute";
22783             s.visibility = "hidden";
22784             s.cursor     = "move";
22785             s.border     = "2px solid #aaa";
22786             s.zIndex     = 999;
22787
22788             // appendChild can blow up IE if invoked prior to the window load event
22789             // while rendering a table.  It is possible there are other scenarios
22790             // that would cause this to happen as well.
22791             body.insertBefore(div, body.firstChild);
22792         }
22793     },
22794
22795     /**
22796      * Initialization for the drag frame element.  Must be called in the
22797      * constructor of all subclasses
22798      * @method initFrame
22799      */
22800     initFrame: function() {
22801         this.createFrame();
22802     },
22803
22804     applyConfig: function() {
22805         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22806
22807         this.resizeFrame = (this.config.resizeFrame !== false);
22808         this.centerFrame = (this.config.centerFrame);
22809         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22810     },
22811
22812     /**
22813      * Resizes the drag frame to the dimensions of the clicked object, positions
22814      * it over the object, and finally displays it
22815      * @method showFrame
22816      * @param {int} iPageX X click position
22817      * @param {int} iPageY Y click position
22818      * @private
22819      */
22820     showFrame: function(iPageX, iPageY) {
22821         var el = this.getEl();
22822         var dragEl = this.getDragEl();
22823         var s = dragEl.style;
22824
22825         this._resizeProxy();
22826
22827         if (this.centerFrame) {
22828             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22829                            Math.round(parseInt(s.height, 10)/2) );
22830         }
22831
22832         this.setDragElPos(iPageX, iPageY);
22833
22834         Roo.fly(dragEl).show();
22835     },
22836
22837     /**
22838      * The proxy is automatically resized to the dimensions of the linked
22839      * element when a drag is initiated, unless resizeFrame is set to false
22840      * @method _resizeProxy
22841      * @private
22842      */
22843     _resizeProxy: function() {
22844         if (this.resizeFrame) {
22845             var el = this.getEl();
22846             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22847         }
22848     },
22849
22850     // overrides Roo.dd.DragDrop
22851     b4MouseDown: function(e) {
22852         var x = e.getPageX();
22853         var y = e.getPageY();
22854         this.autoOffset(x, y);
22855         this.setDragElPos(x, y);
22856     },
22857
22858     // overrides Roo.dd.DragDrop
22859     b4StartDrag: function(x, y) {
22860         // show the drag frame
22861         this.showFrame(x, y);
22862     },
22863
22864     // overrides Roo.dd.DragDrop
22865     b4EndDrag: function(e) {
22866         Roo.fly(this.getDragEl()).hide();
22867     },
22868
22869     // overrides Roo.dd.DragDrop
22870     // By default we try to move the element to the last location of the frame.
22871     // This is so that the default behavior mirrors that of Roo.dd.DD.
22872     endDrag: function(e) {
22873
22874         var lel = this.getEl();
22875         var del = this.getDragEl();
22876
22877         // Show the drag frame briefly so we can get its position
22878         del.style.visibility = "";
22879
22880         this.beforeMove();
22881         // Hide the linked element before the move to get around a Safari
22882         // rendering bug.
22883         lel.style.visibility = "hidden";
22884         Roo.dd.DDM.moveToEl(lel, del);
22885         del.style.visibility = "hidden";
22886         lel.style.visibility = "";
22887
22888         this.afterDrag();
22889     },
22890
22891     beforeMove : function(){
22892
22893     },
22894
22895     afterDrag : function(){
22896
22897     },
22898
22899     toString: function() {
22900         return ("DDProxy " + this.id);
22901     }
22902
22903 });
22904 /*
22905  * Based on:
22906  * Ext JS Library 1.1.1
22907  * Copyright(c) 2006-2007, Ext JS, LLC.
22908  *
22909  * Originally Released Under LGPL - original licence link has changed is not relivant.
22910  *
22911  * Fork - LGPL
22912  * <script type="text/javascript">
22913  */
22914
22915  /**
22916  * @class Roo.dd.DDTarget
22917  * A DragDrop implementation that does not move, but can be a drop
22918  * target.  You would get the same result by simply omitting implementation
22919  * for the event callbacks, but this way we reduce the processing cost of the
22920  * event listener and the callbacks.
22921  * @extends Roo.dd.DragDrop
22922  * @constructor
22923  * @param {String} id the id of the element that is a drop target
22924  * @param {String} sGroup the group of related DragDrop objects
22925  * @param {object} config an object containing configurable attributes
22926  *                 Valid properties for DDTarget in addition to those in
22927  *                 DragDrop:
22928  *                    none
22929  */
22930 Roo.dd.DDTarget = function(id, sGroup, config) {
22931     if (id) {
22932         this.initTarget(id, sGroup, config);
22933     }
22934     if (config && (config.listeners || config.events)) { 
22935         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22936             listeners : config.listeners || {}, 
22937             events : config.events || {} 
22938         });    
22939     }
22940 };
22941
22942 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22943 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22944     toString: function() {
22945         return ("DDTarget " + this.id);
22946     }
22947 });
22948 /*
22949  * Based on:
22950  * Ext JS Library 1.1.1
22951  * Copyright(c) 2006-2007, Ext JS, LLC.
22952  *
22953  * Originally Released Under LGPL - original licence link has changed is not relivant.
22954  *
22955  * Fork - LGPL
22956  * <script type="text/javascript">
22957  */
22958  
22959
22960 /**
22961  * @class Roo.dd.ScrollManager
22962  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22963  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22964  * @static
22965  */
22966 Roo.dd.ScrollManager = function(){
22967     var ddm = Roo.dd.DragDropMgr;
22968     var els = {};
22969     var dragEl = null;
22970     var proc = {};
22971     
22972     
22973     
22974     var onStop = function(e){
22975         dragEl = null;
22976         clearProc();
22977     };
22978     
22979     var triggerRefresh = function(){
22980         if(ddm.dragCurrent){
22981              ddm.refreshCache(ddm.dragCurrent.groups);
22982         }
22983     };
22984     
22985     var doScroll = function(){
22986         if(ddm.dragCurrent){
22987             var dds = Roo.dd.ScrollManager;
22988             if(!dds.animate){
22989                 if(proc.el.scroll(proc.dir, dds.increment)){
22990                     triggerRefresh();
22991                 }
22992             }else{
22993                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22994             }
22995         }
22996     };
22997     
22998     var clearProc = function(){
22999         if(proc.id){
23000             clearInterval(proc.id);
23001         }
23002         proc.id = 0;
23003         proc.el = null;
23004         proc.dir = "";
23005     };
23006     
23007     var startProc = function(el, dir){
23008          Roo.log('scroll startproc');
23009         clearProc();
23010         proc.el = el;
23011         proc.dir = dir;
23012         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
23013     };
23014     
23015     var onFire = function(e, isDrop){
23016        
23017         if(isDrop || !ddm.dragCurrent){ return; }
23018         var dds = Roo.dd.ScrollManager;
23019         if(!dragEl || dragEl != ddm.dragCurrent){
23020             dragEl = ddm.dragCurrent;
23021             // refresh regions on drag start
23022             dds.refreshCache();
23023         }
23024         
23025         var xy = Roo.lib.Event.getXY(e);
23026         var pt = new Roo.lib.Point(xy[0], xy[1]);
23027         for(var id in els){
23028             var el = els[id], r = el._region;
23029             if(r && r.contains(pt) && el.isScrollable()){
23030                 if(r.bottom - pt.y <= dds.thresh){
23031                     if(proc.el != el){
23032                         startProc(el, "down");
23033                     }
23034                     return;
23035                 }else if(r.right - pt.x <= dds.thresh){
23036                     if(proc.el != el){
23037                         startProc(el, "left");
23038                     }
23039                     return;
23040                 }else if(pt.y - r.top <= dds.thresh){
23041                     if(proc.el != el){
23042                         startProc(el, "up");
23043                     }
23044                     return;
23045                 }else if(pt.x - r.left <= dds.thresh){
23046                     if(proc.el != el){
23047                         startProc(el, "right");
23048                     }
23049                     return;
23050                 }
23051             }
23052         }
23053         clearProc();
23054     };
23055     
23056     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
23057     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
23058     
23059     return {
23060         /**
23061          * Registers new overflow element(s) to auto scroll
23062          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
23063          */
23064         register : function(el){
23065             if(el instanceof Array){
23066                 for(var i = 0, len = el.length; i < len; i++) {
23067                         this.register(el[i]);
23068                 }
23069             }else{
23070                 el = Roo.get(el);
23071                 els[el.id] = el;
23072             }
23073             Roo.dd.ScrollManager.els = els;
23074         },
23075         
23076         /**
23077          * Unregisters overflow element(s) so they are no longer scrolled
23078          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
23079          */
23080         unregister : function(el){
23081             if(el instanceof Array){
23082                 for(var i = 0, len = el.length; i < len; i++) {
23083                         this.unregister(el[i]);
23084                 }
23085             }else{
23086                 el = Roo.get(el);
23087                 delete els[el.id];
23088             }
23089         },
23090         
23091         /**
23092          * The number of pixels from the edge of a container the pointer needs to be to 
23093          * trigger scrolling (defaults to 25)
23094          * @type Number
23095          */
23096         thresh : 25,
23097         
23098         /**
23099          * The number of pixels to scroll in each scroll increment (defaults to 50)
23100          * @type Number
23101          */
23102         increment : 100,
23103         
23104         /**
23105          * The frequency of scrolls in milliseconds (defaults to 500)
23106          * @type Number
23107          */
23108         frequency : 500,
23109         
23110         /**
23111          * True to animate the scroll (defaults to true)
23112          * @type Boolean
23113          */
23114         animate: true,
23115         
23116         /**
23117          * The animation duration in seconds - 
23118          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
23119          * @type Number
23120          */
23121         animDuration: .4,
23122         
23123         /**
23124          * Manually trigger a cache refresh.
23125          */
23126         refreshCache : function(){
23127             for(var id in els){
23128                 if(typeof els[id] == 'object'){ // for people extending the object prototype
23129                     els[id]._region = els[id].getRegion();
23130                 }
23131             }
23132         }
23133     };
23134 }();/*
23135  * Based on:
23136  * Ext JS Library 1.1.1
23137  * Copyright(c) 2006-2007, Ext JS, LLC.
23138  *
23139  * Originally Released Under LGPL - original licence link has changed is not relivant.
23140  *
23141  * Fork - LGPL
23142  * <script type="text/javascript">
23143  */
23144  
23145
23146 /**
23147  * @class Roo.dd.Registry
23148  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
23149  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
23150  * @static
23151  */
23152 Roo.dd.Registry = function(){
23153     var elements = {}; 
23154     var handles = {}; 
23155     var autoIdSeed = 0;
23156
23157     var getId = function(el, autogen){
23158         if(typeof el == "string"){
23159             return el;
23160         }
23161         var id = el.id;
23162         if(!id && autogen !== false){
23163             id = "roodd-" + (++autoIdSeed);
23164             el.id = id;
23165         }
23166         return id;
23167     };
23168     
23169     return {
23170     /**
23171      * Register a drag drop element
23172      * @param {String|HTMLElement} element The id or DOM node to register
23173      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
23174      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
23175      * knows how to interpret, plus there are some specific properties known to the Registry that should be
23176      * populated in the data object (if applicable):
23177      * <pre>
23178 Value      Description<br />
23179 ---------  ------------------------------------------<br />
23180 handles    Array of DOM nodes that trigger dragging<br />
23181            for the element being registered<br />
23182 isHandle   True if the element passed in triggers<br />
23183            dragging itself, else false
23184 </pre>
23185      */
23186         register : function(el, data){
23187             data = data || {};
23188             if(typeof el == "string"){
23189                 el = document.getElementById(el);
23190             }
23191             data.ddel = el;
23192             elements[getId(el)] = data;
23193             if(data.isHandle !== false){
23194                 handles[data.ddel.id] = data;
23195             }
23196             if(data.handles){
23197                 var hs = data.handles;
23198                 for(var i = 0, len = hs.length; i < len; i++){
23199                         handles[getId(hs[i])] = data;
23200                 }
23201             }
23202         },
23203
23204     /**
23205      * Unregister a drag drop element
23206      * @param {String|HTMLElement}  element The id or DOM node to unregister
23207      */
23208         unregister : function(el){
23209             var id = getId(el, false);
23210             var data = elements[id];
23211             if(data){
23212                 delete elements[id];
23213                 if(data.handles){
23214                     var hs = data.handles;
23215                     for(var i = 0, len = hs.length; i < len; i++){
23216                         delete handles[getId(hs[i], false)];
23217                     }
23218                 }
23219             }
23220         },
23221
23222     /**
23223      * Returns the handle registered for a DOM Node by id
23224      * @param {String|HTMLElement} id The DOM node or id to look up
23225      * @return {Object} handle The custom handle data
23226      */
23227         getHandle : function(id){
23228             if(typeof id != "string"){ // must be element?
23229                 id = id.id;
23230             }
23231             return handles[id];
23232         },
23233
23234     /**
23235      * Returns the handle that is registered for the DOM node that is the target of the event
23236      * @param {Event} e The event
23237      * @return {Object} handle The custom handle data
23238      */
23239         getHandleFromEvent : function(e){
23240             var t = Roo.lib.Event.getTarget(e);
23241             return t ? handles[t.id] : null;
23242         },
23243
23244     /**
23245      * Returns a custom data object that is registered for a DOM node by id
23246      * @param {String|HTMLElement} id The DOM node or id to look up
23247      * @return {Object} data The custom data
23248      */
23249         getTarget : function(id){
23250             if(typeof id != "string"){ // must be element?
23251                 id = id.id;
23252             }
23253             return elements[id];
23254         },
23255
23256     /**
23257      * Returns a custom data object that is registered for the DOM node that is the target of the event
23258      * @param {Event} e The event
23259      * @return {Object} data The custom data
23260      */
23261         getTargetFromEvent : function(e){
23262             var t = Roo.lib.Event.getTarget(e);
23263             return t ? elements[t.id] || handles[t.id] : null;
23264         }
23265     };
23266 }();/*
23267  * Based on:
23268  * Ext JS Library 1.1.1
23269  * Copyright(c) 2006-2007, Ext JS, LLC.
23270  *
23271  * Originally Released Under LGPL - original licence link has changed is not relivant.
23272  *
23273  * Fork - LGPL
23274  * <script type="text/javascript">
23275  */
23276  
23277
23278 /**
23279  * @class Roo.dd.StatusProxy
23280  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
23281  * default drag proxy used by all Roo.dd components.
23282  * @constructor
23283  * @param {Object} config
23284  */
23285 Roo.dd.StatusProxy = function(config){
23286     Roo.apply(this, config);
23287     this.id = this.id || Roo.id();
23288     this.el = new Roo.Layer({
23289         dh: {
23290             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
23291                 {tag: "div", cls: "x-dd-drop-icon"},
23292                 {tag: "div", cls: "x-dd-drag-ghost"}
23293             ]
23294         }, 
23295         shadow: !config || config.shadow !== false
23296     });
23297     this.ghost = Roo.get(this.el.dom.childNodes[1]);
23298     this.dropStatus = this.dropNotAllowed;
23299 };
23300
23301 Roo.dd.StatusProxy.prototype = {
23302     /**
23303      * @cfg {String} dropAllowed
23304      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
23305      */
23306     dropAllowed : "x-dd-drop-ok",
23307     /**
23308      * @cfg {String} dropNotAllowed
23309      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
23310      */
23311     dropNotAllowed : "x-dd-drop-nodrop",
23312
23313     /**
23314      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
23315      * over the current target element.
23316      * @param {String} cssClass The css class for the new drop status indicator image
23317      */
23318     setStatus : function(cssClass){
23319         cssClass = cssClass || this.dropNotAllowed;
23320         if(this.dropStatus != cssClass){
23321             this.el.replaceClass(this.dropStatus, cssClass);
23322             this.dropStatus = cssClass;
23323         }
23324     },
23325
23326     /**
23327      * Resets the status indicator to the default dropNotAllowed value
23328      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23329      */
23330     reset : function(clearGhost){
23331         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23332         this.dropStatus = this.dropNotAllowed;
23333         if(clearGhost){
23334             this.ghost.update("");
23335         }
23336     },
23337
23338     /**
23339      * Updates the contents of the ghost element
23340      * @param {String} html The html that will replace the current innerHTML of the ghost element
23341      */
23342     update : function(html){
23343         if(typeof html == "string"){
23344             this.ghost.update(html);
23345         }else{
23346             this.ghost.update("");
23347             html.style.margin = "0";
23348             this.ghost.dom.appendChild(html);
23349         }
23350         // ensure float = none set?? cant remember why though.
23351         var el = this.ghost.dom.firstChild;
23352                 if(el){
23353                         Roo.fly(el).setStyle('float', 'none');
23354                 }
23355     },
23356     
23357     /**
23358      * Returns the underlying proxy {@link Roo.Layer}
23359      * @return {Roo.Layer} el
23360     */
23361     getEl : function(){
23362         return this.el;
23363     },
23364
23365     /**
23366      * Returns the ghost element
23367      * @return {Roo.Element} el
23368      */
23369     getGhost : function(){
23370         return this.ghost;
23371     },
23372
23373     /**
23374      * Hides the proxy
23375      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23376      */
23377     hide : function(clear){
23378         this.el.hide();
23379         if(clear){
23380             this.reset(true);
23381         }
23382     },
23383
23384     /**
23385      * Stops the repair animation if it's currently running
23386      */
23387     stop : function(){
23388         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23389             this.anim.stop();
23390         }
23391     },
23392
23393     /**
23394      * Displays this proxy
23395      */
23396     show : function(){
23397         this.el.show();
23398     },
23399
23400     /**
23401      * Force the Layer to sync its shadow and shim positions to the element
23402      */
23403     sync : function(){
23404         this.el.sync();
23405     },
23406
23407     /**
23408      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23409      * invalid drop operation by the item being dragged.
23410      * @param {Array} xy The XY position of the element ([x, y])
23411      * @param {Function} callback The function to call after the repair is complete
23412      * @param {Object} scope The scope in which to execute the callback
23413      */
23414     repair : function(xy, callback, scope){
23415         this.callback = callback;
23416         this.scope = scope;
23417         if(xy && this.animRepair !== false){
23418             this.el.addClass("x-dd-drag-repair");
23419             this.el.hideUnders(true);
23420             this.anim = this.el.shift({
23421                 duration: this.repairDuration || .5,
23422                 easing: 'easeOut',
23423                 xy: xy,
23424                 stopFx: true,
23425                 callback: this.afterRepair,
23426                 scope: this
23427             });
23428         }else{
23429             this.afterRepair();
23430         }
23431     },
23432
23433     // private
23434     afterRepair : function(){
23435         this.hide(true);
23436         if(typeof this.callback == "function"){
23437             this.callback.call(this.scope || this);
23438         }
23439         this.callback = null;
23440         this.scope = null;
23441     }
23442 };/*
23443  * Based on:
23444  * Ext JS Library 1.1.1
23445  * Copyright(c) 2006-2007, Ext JS, LLC.
23446  *
23447  * Originally Released Under LGPL - original licence link has changed is not relivant.
23448  *
23449  * Fork - LGPL
23450  * <script type="text/javascript">
23451  */
23452
23453 /**
23454  * @class Roo.dd.DragSource
23455  * @extends Roo.dd.DDProxy
23456  * A simple class that provides the basic implementation needed to make any element draggable.
23457  * @constructor
23458  * @param {String/HTMLElement/Element} el The container element
23459  * @param {Object} config
23460  */
23461 Roo.dd.DragSource = function(el, config){
23462     this.el = Roo.get(el);
23463     this.dragData = {};
23464     
23465     Roo.apply(this, config);
23466     
23467     if(!this.proxy){
23468         this.proxy = new Roo.dd.StatusProxy();
23469     }
23470
23471     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23472           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23473     
23474     this.dragging = false;
23475 };
23476
23477 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23478     /**
23479      * @cfg {String} dropAllowed
23480      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23481      */
23482     dropAllowed : "x-dd-drop-ok",
23483     /**
23484      * @cfg {String} dropNotAllowed
23485      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23486      */
23487     dropNotAllowed : "x-dd-drop-nodrop",
23488
23489     /**
23490      * Returns the data object associated with this drag source
23491      * @return {Object} data An object containing arbitrary data
23492      */
23493     getDragData : function(e){
23494         return this.dragData;
23495     },
23496
23497     // private
23498     onDragEnter : function(e, id){
23499         var target = Roo.dd.DragDropMgr.getDDById(id);
23500         this.cachedTarget = target;
23501         if(this.beforeDragEnter(target, e, id) !== false){
23502             if(target.isNotifyTarget){
23503                 var status = target.notifyEnter(this, e, this.dragData);
23504                 this.proxy.setStatus(status);
23505             }else{
23506                 this.proxy.setStatus(this.dropAllowed);
23507             }
23508             
23509             if(this.afterDragEnter){
23510                 /**
23511                  * An empty function by default, but provided so that you can perform a custom action
23512                  * when the dragged item enters the drop target by providing an implementation.
23513                  * @param {Roo.dd.DragDrop} target The drop target
23514                  * @param {Event} e The event object
23515                  * @param {String} id The id of the dragged element
23516                  * @method afterDragEnter
23517                  */
23518                 this.afterDragEnter(target, e, id);
23519             }
23520         }
23521     },
23522
23523     /**
23524      * An empty function by default, but provided so that you can perform a custom action
23525      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23526      * @param {Roo.dd.DragDrop} target The drop target
23527      * @param {Event} e The event object
23528      * @param {String} id The id of the dragged element
23529      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23530      */
23531     beforeDragEnter : function(target, e, id){
23532         return true;
23533     },
23534
23535     // private
23536     alignElWithMouse: function() {
23537         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23538         this.proxy.sync();
23539     },
23540
23541     // private
23542     onDragOver : function(e, id){
23543         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23544         if(this.beforeDragOver(target, e, id) !== false){
23545             if(target.isNotifyTarget){
23546                 var status = target.notifyOver(this, e, this.dragData);
23547                 this.proxy.setStatus(status);
23548             }
23549
23550             if(this.afterDragOver){
23551                 /**
23552                  * An empty function by default, but provided so that you can perform a custom action
23553                  * while the dragged item is over the drop target by providing an implementation.
23554                  * @param {Roo.dd.DragDrop} target The drop target
23555                  * @param {Event} e The event object
23556                  * @param {String} id The id of the dragged element
23557                  * @method afterDragOver
23558                  */
23559                 this.afterDragOver(target, e, id);
23560             }
23561         }
23562     },
23563
23564     /**
23565      * An empty function by default, but provided so that you can perform a custom action
23566      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23567      * @param {Roo.dd.DragDrop} target The drop target
23568      * @param {Event} e The event object
23569      * @param {String} id The id of the dragged element
23570      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23571      */
23572     beforeDragOver : function(target, e, id){
23573         return true;
23574     },
23575
23576     // private
23577     onDragOut : function(e, id){
23578         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23579         if(this.beforeDragOut(target, e, id) !== false){
23580             if(target.isNotifyTarget){
23581                 target.notifyOut(this, e, this.dragData);
23582             }
23583             this.proxy.reset();
23584             if(this.afterDragOut){
23585                 /**
23586                  * An empty function by default, but provided so that you can perform a custom action
23587                  * after the dragged item is dragged out of the target without dropping.
23588                  * @param {Roo.dd.DragDrop} target The drop target
23589                  * @param {Event} e The event object
23590                  * @param {String} id The id of the dragged element
23591                  * @method afterDragOut
23592                  */
23593                 this.afterDragOut(target, e, id);
23594             }
23595         }
23596         this.cachedTarget = null;
23597     },
23598
23599     /**
23600      * An empty function by default, but provided so that you can perform a custom action before the dragged
23601      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23602      * @param {Roo.dd.DragDrop} target The drop target
23603      * @param {Event} e The event object
23604      * @param {String} id The id of the dragged element
23605      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23606      */
23607     beforeDragOut : function(target, e, id){
23608         return true;
23609     },
23610     
23611     // private
23612     onDragDrop : function(e, id){
23613         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23614         if(this.beforeDragDrop(target, e, id) !== false){
23615             if(target.isNotifyTarget){
23616                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23617                     this.onValidDrop(target, e, id);
23618                 }else{
23619                     this.onInvalidDrop(target, e, id);
23620                 }
23621             }else{
23622                 this.onValidDrop(target, e, id);
23623             }
23624             
23625             if(this.afterDragDrop){
23626                 /**
23627                  * An empty function by default, but provided so that you can perform a custom action
23628                  * after a valid drag drop has occurred by providing an implementation.
23629                  * @param {Roo.dd.DragDrop} target The drop target
23630                  * @param {Event} e The event object
23631                  * @param {String} id The id of the dropped element
23632                  * @method afterDragDrop
23633                  */
23634                 this.afterDragDrop(target, e, id);
23635             }
23636         }
23637         delete this.cachedTarget;
23638     },
23639
23640     /**
23641      * An empty function by default, but provided so that you can perform a custom action before the dragged
23642      * item is dropped onto the target and optionally cancel the onDragDrop.
23643      * @param {Roo.dd.DragDrop} target The drop target
23644      * @param {Event} e The event object
23645      * @param {String} id The id of the dragged element
23646      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23647      */
23648     beforeDragDrop : function(target, e, id){
23649         return true;
23650     },
23651
23652     // private
23653     onValidDrop : function(target, e, id){
23654         this.hideProxy();
23655         if(this.afterValidDrop){
23656             /**
23657              * An empty function by default, but provided so that you can perform a custom action
23658              * after a valid drop has occurred by providing an implementation.
23659              * @param {Object} target The target DD 
23660              * @param {Event} e The event object
23661              * @param {String} id The id of the dropped element
23662              * @method afterInvalidDrop
23663              */
23664             this.afterValidDrop(target, e, id);
23665         }
23666     },
23667
23668     // private
23669     getRepairXY : function(e, data){
23670         return this.el.getXY();  
23671     },
23672
23673     // private
23674     onInvalidDrop : function(target, e, id){
23675         this.beforeInvalidDrop(target, e, id);
23676         if(this.cachedTarget){
23677             if(this.cachedTarget.isNotifyTarget){
23678                 this.cachedTarget.notifyOut(this, e, this.dragData);
23679             }
23680             this.cacheTarget = null;
23681         }
23682         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23683
23684         if(this.afterInvalidDrop){
23685             /**
23686              * An empty function by default, but provided so that you can perform a custom action
23687              * after an invalid drop has occurred by providing an implementation.
23688              * @param {Event} e The event object
23689              * @param {String} id The id of the dropped element
23690              * @method afterInvalidDrop
23691              */
23692             this.afterInvalidDrop(e, id);
23693         }
23694     },
23695
23696     // private
23697     afterRepair : function(){
23698         if(Roo.enableFx){
23699             this.el.highlight(this.hlColor || "c3daf9");
23700         }
23701         this.dragging = false;
23702     },
23703
23704     /**
23705      * An empty function by default, but provided so that you can perform a custom action after an invalid
23706      * drop has occurred.
23707      * @param {Roo.dd.DragDrop} target The drop target
23708      * @param {Event} e The event object
23709      * @param {String} id The id of the dragged element
23710      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23711      */
23712     beforeInvalidDrop : function(target, e, id){
23713         return true;
23714     },
23715
23716     // private
23717     handleMouseDown : function(e){
23718         if(this.dragging) {
23719             return;
23720         }
23721         var data = this.getDragData(e);
23722         if(data && this.onBeforeDrag(data, e) !== false){
23723             this.dragData = data;
23724             this.proxy.stop();
23725             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23726         } 
23727     },
23728
23729     /**
23730      * An empty function by default, but provided so that you can perform a custom action before the initial
23731      * drag event begins and optionally cancel it.
23732      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23733      * @param {Event} e The event object
23734      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23735      */
23736     onBeforeDrag : function(data, e){
23737         return true;
23738     },
23739
23740     /**
23741      * An empty function by default, but provided so that you can perform a custom action once the initial
23742      * drag event has begun.  The drag cannot be canceled from this function.
23743      * @param {Number} x The x position of the click on the dragged object
23744      * @param {Number} y The y position of the click on the dragged object
23745      */
23746     onStartDrag : Roo.emptyFn,
23747
23748     // private - YUI override
23749     startDrag : function(x, y){
23750         this.proxy.reset();
23751         this.dragging = true;
23752         this.proxy.update("");
23753         this.onInitDrag(x, y);
23754         this.proxy.show();
23755     },
23756
23757     // private
23758     onInitDrag : function(x, y){
23759         var clone = this.el.dom.cloneNode(true);
23760         clone.id = Roo.id(); // prevent duplicate ids
23761         this.proxy.update(clone);
23762         this.onStartDrag(x, y);
23763         return true;
23764     },
23765
23766     /**
23767      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23768      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23769      */
23770     getProxy : function(){
23771         return this.proxy;  
23772     },
23773
23774     /**
23775      * Hides the drag source's {@link Roo.dd.StatusProxy}
23776      */
23777     hideProxy : function(){
23778         this.proxy.hide();  
23779         this.proxy.reset(true);
23780         this.dragging = false;
23781     },
23782
23783     // private
23784     triggerCacheRefresh : function(){
23785         Roo.dd.DDM.refreshCache(this.groups);
23786     },
23787
23788     // private - override to prevent hiding
23789     b4EndDrag: function(e) {
23790     },
23791
23792     // private - override to prevent moving
23793     endDrag : function(e){
23794         this.onEndDrag(this.dragData, e);
23795     },
23796
23797     // private
23798     onEndDrag : function(data, e){
23799     },
23800     
23801     // private - pin to cursor
23802     autoOffset : function(x, y) {
23803         this.setDelta(-12, -20);
23804     }    
23805 });/*
23806  * Based on:
23807  * Ext JS Library 1.1.1
23808  * Copyright(c) 2006-2007, Ext JS, LLC.
23809  *
23810  * Originally Released Under LGPL - original licence link has changed is not relivant.
23811  *
23812  * Fork - LGPL
23813  * <script type="text/javascript">
23814  */
23815
23816
23817 /**
23818  * @class Roo.dd.DropTarget
23819  * @extends Roo.dd.DDTarget
23820  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23821  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23822  * @constructor
23823  * @param {String/HTMLElement/Element} el The container element
23824  * @param {Object} config
23825  */
23826 Roo.dd.DropTarget = function(el, config){
23827     this.el = Roo.get(el);
23828     
23829     var listeners = false; ;
23830     if (config && config.listeners) {
23831         listeners= config.listeners;
23832         delete config.listeners;
23833     }
23834     Roo.apply(this, config);
23835     
23836     if(this.containerScroll){
23837         Roo.dd.ScrollManager.register(this.el);
23838     }
23839     this.addEvents( {
23840          /**
23841          * @scope Roo.dd.DropTarget
23842          */
23843          
23844          /**
23845          * @event enter
23846          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23847          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23848          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23849          * 
23850          * IMPORTANT : it should set  this.valid to true|false
23851          * 
23852          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23853          * @param {Event} e The event
23854          * @param {Object} data An object containing arbitrary data supplied by the drag source
23855          */
23856         "enter" : true,
23857         
23858          /**
23859          * @event over
23860          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23861          * This method will be called on every mouse movement while the drag source is over the drop target.
23862          * This default implementation simply returns the dropAllowed config value.
23863          * 
23864          * IMPORTANT : it should set  this.valid to true|false
23865          * 
23866          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23867          * @param {Event} e The event
23868          * @param {Object} data An object containing arbitrary data supplied by the drag source
23869          
23870          */
23871         "over" : true,
23872         /**
23873          * @event out
23874          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23875          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23876          * overClass (if any) from the drop element.
23877          * 
23878          * 
23879          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23880          * @param {Event} e The event
23881          * @param {Object} data An object containing arbitrary data supplied by the drag source
23882          */
23883          "out" : true,
23884          
23885         /**
23886          * @event drop
23887          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23888          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23889          * implementation that does something to process the drop event and returns true so that the drag source's
23890          * repair action does not run.
23891          * 
23892          * IMPORTANT : it should set this.success
23893          * 
23894          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23895          * @param {Event} e The event
23896          * @param {Object} data An object containing arbitrary data supplied by the drag source
23897         */
23898          "drop" : true
23899     });
23900             
23901      
23902     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23903         this.el.dom, 
23904         this.ddGroup || this.group,
23905         {
23906             isTarget: true,
23907             listeners : listeners || {} 
23908            
23909         
23910         }
23911     );
23912
23913 };
23914
23915 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23916     /**
23917      * @cfg {String} overClass
23918      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23919      */
23920      /**
23921      * @cfg {String} ddGroup
23922      * The drag drop group to handle drop events for
23923      */
23924      
23925     /**
23926      * @cfg {String} dropAllowed
23927      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23928      */
23929     dropAllowed : "x-dd-drop-ok",
23930     /**
23931      * @cfg {String} dropNotAllowed
23932      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23933      */
23934     dropNotAllowed : "x-dd-drop-nodrop",
23935     /**
23936      * @cfg {boolean} success
23937      * set this after drop listener.. 
23938      */
23939     success : false,
23940     /**
23941      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23942      * if the drop point is valid for over/enter..
23943      */
23944     valid : false,
23945     // private
23946     isTarget : true,
23947
23948     // private
23949     isNotifyTarget : true,
23950     
23951     /**
23952      * @hide
23953      */
23954     notifyEnter : function(dd, e, data)
23955     {
23956         this.valid = true;
23957         this.fireEvent('enter', dd, e, data);
23958         if(this.overClass){
23959             this.el.addClass(this.overClass);
23960         }
23961         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23962             this.valid ? this.dropAllowed : this.dropNotAllowed
23963         );
23964     },
23965
23966     /**
23967      * @hide
23968      */
23969     notifyOver : function(dd, e, data)
23970     {
23971         this.valid = true;
23972         this.fireEvent('over', dd, e, data);
23973         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23974             this.valid ? this.dropAllowed : this.dropNotAllowed
23975         );
23976     },
23977
23978     /**
23979      * @hide
23980      */
23981     notifyOut : function(dd, e, data)
23982     {
23983         this.fireEvent('out', dd, e, data);
23984         if(this.overClass){
23985             this.el.removeClass(this.overClass);
23986         }
23987     },
23988
23989     /**
23990      * @hide
23991      */
23992     notifyDrop : function(dd, e, data)
23993     {
23994         this.success = false;
23995         this.fireEvent('drop', dd, e, data);
23996         return this.success;
23997     }
23998 });/*
23999  * Based on:
24000  * Ext JS Library 1.1.1
24001  * Copyright(c) 2006-2007, Ext JS, LLC.
24002  *
24003  * Originally Released Under LGPL - original licence link has changed is not relivant.
24004  *
24005  * Fork - LGPL
24006  * <script type="text/javascript">
24007  */
24008
24009
24010 /**
24011  * @class Roo.dd.DragZone
24012  * @extends Roo.dd.DragSource
24013  * This class provides a container DD instance that proxies for multiple child node sources.<br />
24014  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
24015  * @constructor
24016  * @param {String/HTMLElement/Element} el The container element
24017  * @param {Object} config
24018  */
24019 Roo.dd.DragZone = function(el, config){
24020     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
24021     if(this.containerScroll){
24022         Roo.dd.ScrollManager.register(this.el);
24023     }
24024 };
24025
24026 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
24027     /**
24028      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
24029      * for auto scrolling during drag operations.
24030      */
24031     /**
24032      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
24033      * method after a failed drop (defaults to "c3daf9" - light blue)
24034      */
24035
24036     /**
24037      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
24038      * for a valid target to drag based on the mouse down. Override this method
24039      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
24040      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
24041      * @param {EventObject} e The mouse down event
24042      * @return {Object} The dragData
24043      */
24044     getDragData : function(e){
24045         return Roo.dd.Registry.getHandleFromEvent(e);
24046     },
24047     
24048     /**
24049      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
24050      * this.dragData.ddel
24051      * @param {Number} x The x position of the click on the dragged object
24052      * @param {Number} y The y position of the click on the dragged object
24053      * @return {Boolean} true to continue the drag, false to cancel
24054      */
24055     onInitDrag : function(x, y){
24056         this.proxy.update(this.dragData.ddel.cloneNode(true));
24057         this.onStartDrag(x, y);
24058         return true;
24059     },
24060     
24061     /**
24062      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
24063      */
24064     afterRepair : function(){
24065         if(Roo.enableFx){
24066             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
24067         }
24068         this.dragging = false;
24069     },
24070
24071     /**
24072      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
24073      * the XY of this.dragData.ddel
24074      * @param {EventObject} e The mouse up event
24075      * @return {Array} The xy location (e.g. [100, 200])
24076      */
24077     getRepairXY : function(e){
24078         return Roo.Element.fly(this.dragData.ddel).getXY();  
24079     }
24080 });/*
24081  * Based on:
24082  * Ext JS Library 1.1.1
24083  * Copyright(c) 2006-2007, Ext JS, LLC.
24084  *
24085  * Originally Released Under LGPL - original licence link has changed is not relivant.
24086  *
24087  * Fork - LGPL
24088  * <script type="text/javascript">
24089  */
24090 /**
24091  * @class Roo.dd.DropZone
24092  * @extends Roo.dd.DropTarget
24093  * This class provides a container DD instance that proxies for multiple child node targets.<br />
24094  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
24095  * @constructor
24096  * @param {String/HTMLElement/Element} el The container element
24097  * @param {Object} config
24098  */
24099 Roo.dd.DropZone = function(el, config){
24100     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
24101 };
24102
24103 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
24104     /**
24105      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
24106      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
24107      * provide your own custom lookup.
24108      * @param {Event} e The event
24109      * @return {Object} data The custom data
24110      */
24111     getTargetFromEvent : function(e){
24112         return Roo.dd.Registry.getTargetFromEvent(e);
24113     },
24114
24115     /**
24116      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
24117      * that it has registered.  This method has no default implementation and should be overridden to provide
24118      * node-specific processing if necessary.
24119      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
24120      * {@link #getTargetFromEvent} for this node)
24121      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24122      * @param {Event} e The event
24123      * @param {Object} data An object containing arbitrary data supplied by the drag source
24124      */
24125     onNodeEnter : function(n, dd, e, data){
24126         
24127     },
24128
24129     /**
24130      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
24131      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
24132      * overridden to provide the proper feedback.
24133      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24134      * {@link #getTargetFromEvent} for this node)
24135      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24136      * @param {Event} e The event
24137      * @param {Object} data An object containing arbitrary data supplied by the drag source
24138      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24139      * underlying {@link Roo.dd.StatusProxy} can be updated
24140      */
24141     onNodeOver : function(n, dd, e, data){
24142         return this.dropAllowed;
24143     },
24144
24145     /**
24146      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
24147      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
24148      * node-specific processing if necessary.
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      */
24155     onNodeOut : function(n, dd, e, data){
24156         
24157     },
24158
24159     /**
24160      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
24161      * the drop node.  The default implementation returns false, so it should be overridden to provide the
24162      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
24163      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
24164      * {@link #getTargetFromEvent} for this node)
24165      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24166      * @param {Event} e The event
24167      * @param {Object} data An object containing arbitrary data supplied by the drag source
24168      * @return {Boolean} True if the drop was valid, else false
24169      */
24170     onNodeDrop : function(n, dd, e, data){
24171         return false;
24172     },
24173
24174     /**
24175      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
24176      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
24177      * it should be overridden to provide the proper feedback if necessary.
24178      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24179      * @param {Event} e The event
24180      * @param {Object} data An object containing arbitrary data supplied by the drag source
24181      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24182      * underlying {@link Roo.dd.StatusProxy} can be updated
24183      */
24184     onContainerOver : function(dd, e, data){
24185         return this.dropNotAllowed;
24186     },
24187
24188     /**
24189      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
24190      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
24191      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
24192      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
24193      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24194      * @param {Event} e The event
24195      * @param {Object} data An object containing arbitrary data supplied by the drag source
24196      * @return {Boolean} True if the drop was valid, else false
24197      */
24198     onContainerDrop : function(dd, e, data){
24199         return false;
24200     },
24201
24202     /**
24203      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
24204      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
24205      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
24206      * you should override this method and provide a custom implementation.
24207      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24208      * @param {Event} e The event
24209      * @param {Object} data An object containing arbitrary data supplied by the drag source
24210      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24211      * underlying {@link Roo.dd.StatusProxy} can be updated
24212      */
24213     notifyEnter : function(dd, e, data){
24214         return this.dropNotAllowed;
24215     },
24216
24217     /**
24218      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
24219      * This method will be called on every mouse movement while the drag source is over the drop zone.
24220      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
24221      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
24222      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
24223      * registered node, it will call {@link #onContainerOver}.
24224      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24225      * @param {Event} e The event
24226      * @param {Object} data An object containing arbitrary data supplied by the drag source
24227      * @return {String} status The CSS class that communicates the drop status back to the source so that the
24228      * underlying {@link Roo.dd.StatusProxy} can be updated
24229      */
24230     notifyOver : function(dd, e, data){
24231         var n = this.getTargetFromEvent(e);
24232         if(!n){ // not over valid drop target
24233             if(this.lastOverNode){
24234                 this.onNodeOut(this.lastOverNode, dd, e, data);
24235                 this.lastOverNode = null;
24236             }
24237             return this.onContainerOver(dd, e, data);
24238         }
24239         if(this.lastOverNode != n){
24240             if(this.lastOverNode){
24241                 this.onNodeOut(this.lastOverNode, dd, e, data);
24242             }
24243             this.onNodeEnter(n, dd, e, data);
24244             this.lastOverNode = n;
24245         }
24246         return this.onNodeOver(n, dd, e, data);
24247     },
24248
24249     /**
24250      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
24251      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
24252      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
24253      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
24254      * @param {Event} e The event
24255      * @param {Object} data An object containing arbitrary data supplied by the drag zone
24256      */
24257     notifyOut : function(dd, e, data){
24258         if(this.lastOverNode){
24259             this.onNodeOut(this.lastOverNode, dd, e, data);
24260             this.lastOverNode = null;
24261         }
24262     },
24263
24264     /**
24265      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
24266      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
24267      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
24268      * otherwise it will call {@link #onContainerDrop}.
24269      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
24270      * @param {Event} e The event
24271      * @param {Object} data An object containing arbitrary data supplied by the drag source
24272      * @return {Boolean} True if the drop was valid, else false
24273      */
24274     notifyDrop : function(dd, e, data){
24275         if(this.lastOverNode){
24276             this.onNodeOut(this.lastOverNode, dd, e, data);
24277             this.lastOverNode = null;
24278         }
24279         var n = this.getTargetFromEvent(e);
24280         return n ?
24281             this.onNodeDrop(n, dd, e, data) :
24282             this.onContainerDrop(dd, e, data);
24283     },
24284
24285     // private
24286     triggerCacheRefresh : function(){
24287         Roo.dd.DDM.refreshCache(this.groups);
24288     }  
24289 });/*
24290  * Based on:
24291  * Ext JS Library 1.1.1
24292  * Copyright(c) 2006-2007, Ext JS, LLC.
24293  *
24294  * Originally Released Under LGPL - original licence link has changed is not relivant.
24295  *
24296  * Fork - LGPL
24297  * <script type="text/javascript">
24298  */
24299
24300
24301 /**
24302  * @class Roo.data.SortTypes
24303  * @static
24304  * Defines the default sorting (casting?) comparison functions used when sorting data.
24305  */
24306 Roo.data.SortTypes = {
24307     /**
24308      * Default sort that does nothing
24309      * @param {Mixed} s The value being converted
24310      * @return {Mixed} The comparison value
24311      */
24312     none : function(s){
24313         return s;
24314     },
24315     
24316     /**
24317      * The regular expression used to strip tags
24318      * @type {RegExp}
24319      * @property
24320      */
24321     stripTagsRE : /<\/?[^>]+>/gi,
24322     
24323     /**
24324      * Strips all HTML tags to sort on text only
24325      * @param {Mixed} s The value being converted
24326      * @return {String} The comparison value
24327      */
24328     asText : function(s){
24329         return String(s).replace(this.stripTagsRE, "");
24330     },
24331     
24332     /**
24333      * Strips all HTML tags to sort on text only - Case insensitive
24334      * @param {Mixed} s The value being converted
24335      * @return {String} The comparison value
24336      */
24337     asUCText : function(s){
24338         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24339     },
24340     
24341     /**
24342      * Case insensitive string
24343      * @param {Mixed} s The value being converted
24344      * @return {String} The comparison value
24345      */
24346     asUCString : function(s) {
24347         return String(s).toUpperCase();
24348     },
24349     
24350     /**
24351      * Date sorting
24352      * @param {Mixed} s The value being converted
24353      * @return {Number} The comparison value
24354      */
24355     asDate : function(s) {
24356         if(!s){
24357             return 0;
24358         }
24359         if(s instanceof Date){
24360             return s.getTime();
24361         }
24362         return Date.parse(String(s));
24363     },
24364     
24365     /**
24366      * Float sorting
24367      * @param {Mixed} s The value being converted
24368      * @return {Float} The comparison value
24369      */
24370     asFloat : function(s) {
24371         var val = parseFloat(String(s).replace(/,/g, ""));
24372         if(isNaN(val)) {
24373             val = 0;
24374         }
24375         return val;
24376     },
24377     
24378     /**
24379      * Integer sorting
24380      * @param {Mixed} s The value being converted
24381      * @return {Number} The comparison value
24382      */
24383     asInt : function(s) {
24384         var val = parseInt(String(s).replace(/,/g, ""));
24385         if(isNaN(val)) {
24386             val = 0;
24387         }
24388         return val;
24389     }
24390 };/*
24391  * Based on:
24392  * Ext JS Library 1.1.1
24393  * Copyright(c) 2006-2007, Ext JS, LLC.
24394  *
24395  * Originally Released Under LGPL - original licence link has changed is not relivant.
24396  *
24397  * Fork - LGPL
24398  * <script type="text/javascript">
24399  */
24400
24401 /**
24402 * @class Roo.data.Record
24403  * Instances of this class encapsulate both record <em>definition</em> information, and record
24404  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24405  * to access Records cached in an {@link Roo.data.Store} object.<br>
24406  * <p>
24407  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24408  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24409  * objects.<br>
24410  * <p>
24411  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24412  * @constructor
24413  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24414  * {@link #create}. The parameters are the same.
24415  * @param {Array} data An associative Array of data values keyed by the field name.
24416  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24417  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24418  * not specified an integer id is generated.
24419  */
24420 Roo.data.Record = function(data, id){
24421     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24422     this.data = data;
24423 };
24424
24425 /**
24426  * Generate a constructor for a specific record layout.
24427  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24428  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24429  * Each field definition object may contain the following properties: <ul>
24430  * <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,
24431  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24432  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24433  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24434  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24435  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24436  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24437  * this may be omitted.</p></li>
24438  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24439  * <ul><li>auto (Default, implies no conversion)</li>
24440  * <li>string</li>
24441  * <li>int</li>
24442  * <li>float</li>
24443  * <li>boolean</li>
24444  * <li>date</li></ul></p></li>
24445  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24446  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24447  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24448  * by the Reader into an object that will be stored in the Record. It is passed the
24449  * following parameters:<ul>
24450  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24451  * </ul></p></li>
24452  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24453  * </ul>
24454  * <br>usage:<br><pre><code>
24455 var TopicRecord = Roo.data.Record.create(
24456     {name: 'title', mapping: 'topic_title'},
24457     {name: 'author', mapping: 'username'},
24458     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24459     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24460     {name: 'lastPoster', mapping: 'user2'},
24461     {name: 'excerpt', mapping: 'post_text'}
24462 );
24463
24464 var myNewRecord = new TopicRecord({
24465     title: 'Do my job please',
24466     author: 'noobie',
24467     totalPosts: 1,
24468     lastPost: new Date(),
24469     lastPoster: 'Animal',
24470     excerpt: 'No way dude!'
24471 });
24472 myStore.add(myNewRecord);
24473 </code></pre>
24474  * @method create
24475  * @static
24476  */
24477 Roo.data.Record.create = function(o){
24478     var f = function(){
24479         f.superclass.constructor.apply(this, arguments);
24480     };
24481     Roo.extend(f, Roo.data.Record);
24482     var p = f.prototype;
24483     p.fields = new Roo.util.MixedCollection(false, function(field){
24484         return field.name;
24485     });
24486     for(var i = 0, len = o.length; i < len; i++){
24487         p.fields.add(new Roo.data.Field(o[i]));
24488     }
24489     f.getField = function(name){
24490         return p.fields.get(name);  
24491     };
24492     return f;
24493 };
24494
24495 Roo.data.Record.AUTO_ID = 1000;
24496 Roo.data.Record.EDIT = 'edit';
24497 Roo.data.Record.REJECT = 'reject';
24498 Roo.data.Record.COMMIT = 'commit';
24499
24500 Roo.data.Record.prototype = {
24501     /**
24502      * Readonly flag - true if this record has been modified.
24503      * @type Boolean
24504      */
24505     dirty : false,
24506     editing : false,
24507     error: null,
24508     modified: null,
24509
24510     // private
24511     join : function(store){
24512         this.store = store;
24513     },
24514
24515     /**
24516      * Set the named field to the specified value.
24517      * @param {String} name The name of the field to set.
24518      * @param {Object} value The value to set the field to.
24519      */
24520     set : function(name, value){
24521         if(this.data[name] == value){
24522             return;
24523         }
24524         this.dirty = true;
24525         if(!this.modified){
24526             this.modified = {};
24527         }
24528         if(typeof this.modified[name] == 'undefined'){
24529             this.modified[name] = this.data[name];
24530         }
24531         this.data[name] = value;
24532         if(!this.editing && this.store){
24533             this.store.afterEdit(this);
24534         }       
24535     },
24536
24537     /**
24538      * Get the value of the named field.
24539      * @param {String} name The name of the field to get the value of.
24540      * @return {Object} The value of the field.
24541      */
24542     get : function(name){
24543         return this.data[name]; 
24544     },
24545
24546     // private
24547     beginEdit : function(){
24548         this.editing = true;
24549         this.modified = {}; 
24550     },
24551
24552     // private
24553     cancelEdit : function(){
24554         this.editing = false;
24555         delete this.modified;
24556     },
24557
24558     // private
24559     endEdit : function(){
24560         this.editing = false;
24561         if(this.dirty && this.store){
24562             this.store.afterEdit(this);
24563         }
24564     },
24565
24566     /**
24567      * Usually called by the {@link Roo.data.Store} which owns the Record.
24568      * Rejects all changes made to the Record since either creation, or the last commit operation.
24569      * Modified fields are reverted to their original values.
24570      * <p>
24571      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24572      * of reject operations.
24573      */
24574     reject : function(){
24575         var m = this.modified;
24576         for(var n in m){
24577             if(typeof m[n] != "function"){
24578                 this.data[n] = m[n];
24579             }
24580         }
24581         this.dirty = false;
24582         delete this.modified;
24583         this.editing = false;
24584         if(this.store){
24585             this.store.afterReject(this);
24586         }
24587     },
24588
24589     /**
24590      * Usually called by the {@link Roo.data.Store} which owns the Record.
24591      * Commits all changes made to the Record since either creation, or the last commit operation.
24592      * <p>
24593      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24594      * of commit operations.
24595      */
24596     commit : function(){
24597         this.dirty = false;
24598         delete this.modified;
24599         this.editing = false;
24600         if(this.store){
24601             this.store.afterCommit(this);
24602         }
24603     },
24604
24605     // private
24606     hasError : function(){
24607         return this.error != null;
24608     },
24609
24610     // private
24611     clearError : function(){
24612         this.error = null;
24613     },
24614
24615     /**
24616      * Creates a copy of this record.
24617      * @param {String} id (optional) A new record id if you don't want to use this record's id
24618      * @return {Record}
24619      */
24620     copy : function(newId) {
24621         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24622     }
24623 };/*
24624  * Based on:
24625  * Ext JS Library 1.1.1
24626  * Copyright(c) 2006-2007, Ext JS, LLC.
24627  *
24628  * Originally Released Under LGPL - original licence link has changed is not relivant.
24629  *
24630  * Fork - LGPL
24631  * <script type="text/javascript">
24632  */
24633
24634
24635
24636 /**
24637  * @class Roo.data.Store
24638  * @extends Roo.util.Observable
24639  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24640  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24641  * <p>
24642  * 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
24643  * has no knowledge of the format of the data returned by the Proxy.<br>
24644  * <p>
24645  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24646  * instances from the data object. These records are cached and made available through accessor functions.
24647  * @constructor
24648  * Creates a new Store.
24649  * @param {Object} config A config object containing the objects needed for the Store to access data,
24650  * and read the data into Records.
24651  */
24652 Roo.data.Store = function(config){
24653     this.data = new Roo.util.MixedCollection(false);
24654     this.data.getKey = function(o){
24655         return o.id;
24656     };
24657     this.baseParams = {};
24658     // private
24659     this.paramNames = {
24660         "start" : "start",
24661         "limit" : "limit",
24662         "sort" : "sort",
24663         "dir" : "dir",
24664         "multisort" : "_multisort"
24665     };
24666
24667     if(config && config.data){
24668         this.inlineData = config.data;
24669         delete config.data;
24670     }
24671
24672     Roo.apply(this, config);
24673     
24674     if(this.reader){ // reader passed
24675         this.reader = Roo.factory(this.reader, Roo.data);
24676         this.reader.xmodule = this.xmodule || false;
24677         if(!this.recordType){
24678             this.recordType = this.reader.recordType;
24679         }
24680         if(this.reader.onMetaChange){
24681             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24682         }
24683     }
24684
24685     if(this.recordType){
24686         this.fields = this.recordType.prototype.fields;
24687     }
24688     this.modified = [];
24689
24690     this.addEvents({
24691         /**
24692          * @event datachanged
24693          * Fires when the data cache has changed, and a widget which is using this Store
24694          * as a Record cache should refresh its view.
24695          * @param {Store} this
24696          */
24697         datachanged : true,
24698         /**
24699          * @event metachange
24700          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24701          * @param {Store} this
24702          * @param {Object} meta The JSON metadata
24703          */
24704         metachange : true,
24705         /**
24706          * @event add
24707          * Fires when Records have been added to the Store
24708          * @param {Store} this
24709          * @param {Roo.data.Record[]} records The array of Records added
24710          * @param {Number} index The index at which the record(s) were added
24711          */
24712         add : true,
24713         /**
24714          * @event remove
24715          * Fires when a Record has been removed from the Store
24716          * @param {Store} this
24717          * @param {Roo.data.Record} record The Record that was removed
24718          * @param {Number} index The index at which the record was removed
24719          */
24720         remove : true,
24721         /**
24722          * @event update
24723          * Fires when a Record has been updated
24724          * @param {Store} this
24725          * @param {Roo.data.Record} record The Record that was updated
24726          * @param {String} operation The update operation being performed.  Value may be one of:
24727          * <pre><code>
24728  Roo.data.Record.EDIT
24729  Roo.data.Record.REJECT
24730  Roo.data.Record.COMMIT
24731          * </code></pre>
24732          */
24733         update : true,
24734         /**
24735          * @event clear
24736          * Fires when the data cache has been cleared.
24737          * @param {Store} this
24738          */
24739         clear : true,
24740         /**
24741          * @event beforeload
24742          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24743          * the load action will be canceled.
24744          * @param {Store} this
24745          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24746          */
24747         beforeload : true,
24748         /**
24749          * @event beforeloadadd
24750          * Fires after a new set of Records has been loaded.
24751          * @param {Store} this
24752          * @param {Roo.data.Record[]} records The Records that were loaded
24753          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24754          */
24755         beforeloadadd : true,
24756         /**
24757          * @event load
24758          * Fires after a new set of Records has been loaded, before they are added to the store.
24759          * @param {Store} this
24760          * @param {Roo.data.Record[]} records The Records that were loaded
24761          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24762          * @params {Object} return from reader
24763          */
24764         load : true,
24765         /**
24766          * @event loadexception
24767          * Fires if an exception occurs in the Proxy during loading.
24768          * Called with the signature of the Proxy's "loadexception" event.
24769          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24770          * 
24771          * @param {Proxy} 
24772          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24773          * @param {Object} load options 
24774          * @param {Object} jsonData from your request (normally this contains the Exception)
24775          */
24776         loadexception : true
24777     });
24778     
24779     if(this.proxy){
24780         this.proxy = Roo.factory(this.proxy, Roo.data);
24781         this.proxy.xmodule = this.xmodule || false;
24782         this.relayEvents(this.proxy,  ["loadexception"]);
24783     }
24784     this.sortToggle = {};
24785     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24786
24787     Roo.data.Store.superclass.constructor.call(this);
24788
24789     if(this.inlineData){
24790         this.loadData(this.inlineData);
24791         delete this.inlineData;
24792     }
24793 };
24794
24795 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24796      /**
24797     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24798     * without a remote query - used by combo/forms at present.
24799     */
24800     
24801     /**
24802     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24803     */
24804     /**
24805     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24806     */
24807     /**
24808     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24809     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24810     */
24811     /**
24812     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24813     * on any HTTP request
24814     */
24815     /**
24816     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24817     */
24818     /**
24819     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24820     */
24821     multiSort: false,
24822     /**
24823     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24824     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24825     */
24826     remoteSort : false,
24827
24828     /**
24829     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24830      * loaded or when a record is removed. (defaults to false).
24831     */
24832     pruneModifiedRecords : false,
24833
24834     // private
24835     lastOptions : null,
24836
24837     /**
24838      * Add Records to the Store and fires the add event.
24839      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24840      */
24841     add : function(records){
24842         records = [].concat(records);
24843         for(var i = 0, len = records.length; i < len; i++){
24844             records[i].join(this);
24845         }
24846         var index = this.data.length;
24847         this.data.addAll(records);
24848         this.fireEvent("add", this, records, index);
24849     },
24850
24851     /**
24852      * Remove a Record from the Store and fires the remove event.
24853      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24854      */
24855     remove : function(record){
24856         var index = this.data.indexOf(record);
24857         this.data.removeAt(index);
24858  
24859         if(this.pruneModifiedRecords){
24860             this.modified.remove(record);
24861         }
24862         this.fireEvent("remove", this, record, index);
24863     },
24864
24865     /**
24866      * Remove all Records from the Store and fires the clear event.
24867      */
24868     removeAll : function(){
24869         this.data.clear();
24870         if(this.pruneModifiedRecords){
24871             this.modified = [];
24872         }
24873         this.fireEvent("clear", this);
24874     },
24875
24876     /**
24877      * Inserts Records to the Store at the given index and fires the add event.
24878      * @param {Number} index The start index at which to insert the passed Records.
24879      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24880      */
24881     insert : function(index, records){
24882         records = [].concat(records);
24883         for(var i = 0, len = records.length; i < len; i++){
24884             this.data.insert(index, records[i]);
24885             records[i].join(this);
24886         }
24887         this.fireEvent("add", this, records, index);
24888     },
24889
24890     /**
24891      * Get the index within the cache of the passed Record.
24892      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24893      * @return {Number} The index of the passed Record. Returns -1 if not found.
24894      */
24895     indexOf : function(record){
24896         return this.data.indexOf(record);
24897     },
24898
24899     /**
24900      * Get the index within the cache of the Record with the passed id.
24901      * @param {String} id The id of the Record to find.
24902      * @return {Number} The index of the Record. Returns -1 if not found.
24903      */
24904     indexOfId : function(id){
24905         return this.data.indexOfKey(id);
24906     },
24907
24908     /**
24909      * Get the Record with the specified id.
24910      * @param {String} id The id of the Record to find.
24911      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24912      */
24913     getById : function(id){
24914         return this.data.key(id);
24915     },
24916
24917     /**
24918      * Get the Record at the specified index.
24919      * @param {Number} index The index of the Record to find.
24920      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24921      */
24922     getAt : function(index){
24923         return this.data.itemAt(index);
24924     },
24925
24926     /**
24927      * Returns a range of Records between specified indices.
24928      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24929      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24930      * @return {Roo.data.Record[]} An array of Records
24931      */
24932     getRange : function(start, end){
24933         return this.data.getRange(start, end);
24934     },
24935
24936     // private
24937     storeOptions : function(o){
24938         o = Roo.apply({}, o);
24939         delete o.callback;
24940         delete o.scope;
24941         this.lastOptions = o;
24942     },
24943
24944     /**
24945      * Loads the Record cache from the configured Proxy using the configured Reader.
24946      * <p>
24947      * If using remote paging, then the first load call must specify the <em>start</em>
24948      * and <em>limit</em> properties in the options.params property to establish the initial
24949      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24950      * <p>
24951      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24952      * and this call will return before the new data has been loaded. Perform any post-processing
24953      * in a callback function, or in a "load" event handler.</strong>
24954      * <p>
24955      * @param {Object} options An object containing properties which control loading options:<ul>
24956      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24957      * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
24958      * <pre>
24959                 {
24960                     data : data,  // array of key=>value data like JsonReader
24961                     total : data.length,
24962                     success : true
24963                     
24964                 }
24965         </pre>
24966             }.</li>
24967      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24968      * passed the following arguments:<ul>
24969      * <li>r : Roo.data.Record[]</li>
24970      * <li>options: Options object from the load call</li>
24971      * <li>success: Boolean success indicator</li></ul></li>
24972      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24973      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24974      * </ul>
24975      */
24976     load : function(options){
24977         options = options || {};
24978         if(this.fireEvent("beforeload", this, options) !== false){
24979             this.storeOptions(options);
24980             var p = Roo.apply(options.params || {}, this.baseParams);
24981             // if meta was not loaded from remote source.. try requesting it.
24982             if (!this.reader.metaFromRemote) {
24983                 p._requestMeta = 1;
24984             }
24985             if(this.sortInfo && this.remoteSort){
24986                 var pn = this.paramNames;
24987                 p[pn["sort"]] = this.sortInfo.field;
24988                 p[pn["dir"]] = this.sortInfo.direction;
24989             }
24990             if (this.multiSort) {
24991                 var pn = this.paramNames;
24992                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24993             }
24994             
24995             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24996         }
24997     },
24998
24999     /**
25000      * Reloads the Record cache from the configured Proxy using the configured Reader and
25001      * the options from the last load operation performed.
25002      * @param {Object} options (optional) An object containing properties which may override the options
25003      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
25004      * the most recently used options are reused).
25005      */
25006     reload : function(options){
25007         this.load(Roo.applyIf(options||{}, this.lastOptions));
25008     },
25009
25010     // private
25011     // Called as a callback by the Reader during a load operation.
25012     loadRecords : function(o, options, success){
25013          
25014         if(!o){
25015             if(success !== false){
25016                 this.fireEvent("load", this, [], options, o);
25017             }
25018             if(options.callback){
25019                 options.callback.call(options.scope || this, [], options, false);
25020             }
25021             return;
25022         }
25023         // if data returned failure - throw an exception.
25024         if (o.success === false) {
25025             // show a message if no listener is registered.
25026             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
25027                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
25028             }
25029             // loadmask wil be hooked into this..
25030             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
25031             return;
25032         }
25033         var r = o.records, t = o.totalRecords || r.length;
25034         
25035         this.fireEvent("beforeloadadd", this, r, options, o);
25036         
25037         if(!options || options.add !== true){
25038             if(this.pruneModifiedRecords){
25039                 this.modified = [];
25040             }
25041             for(var i = 0, len = r.length; i < len; i++){
25042                 r[i].join(this);
25043             }
25044             if(this.snapshot){
25045                 this.data = this.snapshot;
25046                 delete this.snapshot;
25047             }
25048             this.data.clear();
25049             this.data.addAll(r);
25050             this.totalLength = t;
25051             this.applySort();
25052             this.fireEvent("datachanged", this);
25053         }else{
25054             this.totalLength = Math.max(t, this.data.length+r.length);
25055             this.add(r);
25056         }
25057         
25058         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
25059                 
25060             var e = new Roo.data.Record({});
25061
25062             e.set(this.parent.displayField, this.parent.emptyTitle);
25063             e.set(this.parent.valueField, '');
25064
25065             this.insert(0, e);
25066         }
25067             
25068         this.fireEvent("load", this, r, options, o);
25069         if(options.callback){
25070             options.callback.call(options.scope || this, r, options, true);
25071         }
25072     },
25073
25074
25075     /**
25076      * Loads data from a passed data block. A Reader which understands the format of the data
25077      * must have been configured in the constructor.
25078      * @param {Object} data The data block from which to read the Records.  The format of the data expected
25079      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
25080      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
25081      */
25082     loadData : function(o, append){
25083         var r = this.reader.readRecords(o);
25084         this.loadRecords(r, {add: append}, true);
25085     },
25086     
25087      /**
25088      * using 'cn' the nested child reader read the child array into it's child stores.
25089      * @param {Object} rec The record with a 'children array
25090      */
25091     loadDataFromChildren : function(rec)
25092     {
25093         this.loadData(this.reader.toLoadData(rec));
25094     },
25095     
25096
25097     /**
25098      * Gets the number of cached records.
25099      * <p>
25100      * <em>If using paging, this may not be the total size of the dataset. If the data object
25101      * used by the Reader contains the dataset size, then the getTotalCount() function returns
25102      * the data set size</em>
25103      */
25104     getCount : function(){
25105         return this.data.length || 0;
25106     },
25107
25108     /**
25109      * Gets the total number of records in the dataset as returned by the server.
25110      * <p>
25111      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
25112      * the dataset size</em>
25113      */
25114     getTotalCount : function(){
25115         return this.totalLength || 0;
25116     },
25117
25118     /**
25119      * Returns the sort state of the Store as an object with two properties:
25120      * <pre><code>
25121  field {String} The name of the field by which the Records are sorted
25122  direction {String} The sort order, "ASC" or "DESC"
25123      * </code></pre>
25124      */
25125     getSortState : function(){
25126         return this.sortInfo;
25127     },
25128
25129     // private
25130     applySort : function(){
25131         if(this.sortInfo && !this.remoteSort){
25132             var s = this.sortInfo, f = s.field;
25133             var st = this.fields.get(f).sortType;
25134             var fn = function(r1, r2){
25135                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
25136                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
25137             };
25138             this.data.sort(s.direction, fn);
25139             if(this.snapshot && this.snapshot != this.data){
25140                 this.snapshot.sort(s.direction, fn);
25141             }
25142         }
25143     },
25144
25145     /**
25146      * Sets the default sort column and order to be used by the next load operation.
25147      * @param {String} fieldName The name of the field to sort by.
25148      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25149      */
25150     setDefaultSort : function(field, dir){
25151         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
25152     },
25153
25154     /**
25155      * Sort the Records.
25156      * If remote sorting is used, the sort is performed on the server, and the cache is
25157      * reloaded. If local sorting is used, the cache is sorted internally.
25158      * @param {String} fieldName The name of the field to sort by.
25159      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
25160      */
25161     sort : function(fieldName, dir){
25162         var f = this.fields.get(fieldName);
25163         if(!dir){
25164             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
25165             
25166             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
25167                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
25168             }else{
25169                 dir = f.sortDir;
25170             }
25171         }
25172         this.sortToggle[f.name] = dir;
25173         this.sortInfo = {field: f.name, direction: dir};
25174         if(!this.remoteSort){
25175             this.applySort();
25176             this.fireEvent("datachanged", this);
25177         }else{
25178             this.load(this.lastOptions);
25179         }
25180     },
25181
25182     /**
25183      * Calls the specified function for each of the Records in the cache.
25184      * @param {Function} fn The function to call. The Record is passed as the first parameter.
25185      * Returning <em>false</em> aborts and exits the iteration.
25186      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
25187      */
25188     each : function(fn, scope){
25189         this.data.each(fn, scope);
25190     },
25191
25192     /**
25193      * Gets all records modified since the last commit.  Modified records are persisted across load operations
25194      * (e.g., during paging).
25195      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
25196      */
25197     getModifiedRecords : function(){
25198         return this.modified;
25199     },
25200
25201     // private
25202     createFilterFn : function(property, value, anyMatch){
25203         if(!value.exec){ // not a regex
25204             value = String(value);
25205             if(value.length == 0){
25206                 return false;
25207             }
25208             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
25209         }
25210         return function(r){
25211             return value.test(r.data[property]);
25212         };
25213     },
25214
25215     /**
25216      * Sums the value of <i>property</i> for each record between start and end and returns the result.
25217      * @param {String} property A field on your records
25218      * @param {Number} start The record index to start at (defaults to 0)
25219      * @param {Number} end The last record index to include (defaults to length - 1)
25220      * @return {Number} The sum
25221      */
25222     sum : function(property, start, end){
25223         var rs = this.data.items, v = 0;
25224         start = start || 0;
25225         end = (end || end === 0) ? end : rs.length-1;
25226
25227         for(var i = start; i <= end; i++){
25228             v += (rs[i].data[property] || 0);
25229         }
25230         return v;
25231     },
25232
25233     /**
25234      * Filter the records by a specified property.
25235      * @param {String} field A field on your records
25236      * @param {String/RegExp} value Either a string that the field
25237      * should start with or a RegExp to test against the field
25238      * @param {Boolean} anyMatch True to match any part not just the beginning
25239      */
25240     filter : function(property, value, anyMatch){
25241         var fn = this.createFilterFn(property, value, anyMatch);
25242         return fn ? this.filterBy(fn) : this.clearFilter();
25243     },
25244
25245     /**
25246      * Filter by a function. The specified function will be called with each
25247      * record in this data source. If the function returns true the record is included,
25248      * otherwise it is filtered.
25249      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25250      * @param {Object} scope (optional) The scope of the function (defaults to this)
25251      */
25252     filterBy : function(fn, scope){
25253         this.snapshot = this.snapshot || this.data;
25254         this.data = this.queryBy(fn, scope||this);
25255         this.fireEvent("datachanged", this);
25256     },
25257
25258     /**
25259      * Query the records by a specified property.
25260      * @param {String} field A field on your records
25261      * @param {String/RegExp} value Either a string that the field
25262      * should start with or a RegExp to test against the field
25263      * @param {Boolean} anyMatch True to match any part not just the beginning
25264      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25265      */
25266     query : function(property, value, anyMatch){
25267         var fn = this.createFilterFn(property, value, anyMatch);
25268         return fn ? this.queryBy(fn) : this.data.clone();
25269     },
25270
25271     /**
25272      * Query by a function. The specified function will be called with each
25273      * record in this data source. If the function returns true the record is included
25274      * in the results.
25275      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
25276      * @param {Object} scope (optional) The scope of the function (defaults to this)
25277       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
25278      **/
25279     queryBy : function(fn, scope){
25280         var data = this.snapshot || this.data;
25281         return data.filterBy(fn, scope||this);
25282     },
25283
25284     /**
25285      * Collects unique values for a particular dataIndex from this store.
25286      * @param {String} dataIndex The property to collect
25287      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
25288      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
25289      * @return {Array} An array of the unique values
25290      **/
25291     collect : function(dataIndex, allowNull, bypassFilter){
25292         var d = (bypassFilter === true && this.snapshot) ?
25293                 this.snapshot.items : this.data.items;
25294         var v, sv, r = [], l = {};
25295         for(var i = 0, len = d.length; i < len; i++){
25296             v = d[i].data[dataIndex];
25297             sv = String(v);
25298             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
25299                 l[sv] = true;
25300                 r[r.length] = v;
25301             }
25302         }
25303         return r;
25304     },
25305
25306     /**
25307      * Revert to a view of the Record cache with no filtering applied.
25308      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
25309      */
25310     clearFilter : function(suppressEvent){
25311         if(this.snapshot && this.snapshot != this.data){
25312             this.data = this.snapshot;
25313             delete this.snapshot;
25314             if(suppressEvent !== true){
25315                 this.fireEvent("datachanged", this);
25316             }
25317         }
25318     },
25319
25320     // private
25321     afterEdit : function(record){
25322         if(this.modified.indexOf(record) == -1){
25323             this.modified.push(record);
25324         }
25325         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
25326     },
25327     
25328     // private
25329     afterReject : function(record){
25330         this.modified.remove(record);
25331         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25332     },
25333
25334     // private
25335     afterCommit : function(record){
25336         this.modified.remove(record);
25337         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25338     },
25339
25340     /**
25341      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25342      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25343      */
25344     commitChanges : function(){
25345         var m = this.modified.slice(0);
25346         this.modified = [];
25347         for(var i = 0, len = m.length; i < len; i++){
25348             m[i].commit();
25349         }
25350     },
25351
25352     /**
25353      * Cancel outstanding changes on all changed records.
25354      */
25355     rejectChanges : function(){
25356         var m = this.modified.slice(0);
25357         this.modified = [];
25358         for(var i = 0, len = m.length; i < len; i++){
25359             m[i].reject();
25360         }
25361     },
25362
25363     onMetaChange : function(meta, rtype, o){
25364         this.recordType = rtype;
25365         this.fields = rtype.prototype.fields;
25366         delete this.snapshot;
25367         this.sortInfo = meta.sortInfo || this.sortInfo;
25368         this.modified = [];
25369         this.fireEvent('metachange', this, this.reader.meta);
25370     },
25371     
25372     moveIndex : function(data, type)
25373     {
25374         var index = this.indexOf(data);
25375         
25376         var newIndex = index + type;
25377         
25378         this.remove(data);
25379         
25380         this.insert(newIndex, data);
25381         
25382     }
25383 });/*
25384  * Based on:
25385  * Ext JS Library 1.1.1
25386  * Copyright(c) 2006-2007, Ext JS, LLC.
25387  *
25388  * Originally Released Under LGPL - original licence link has changed is not relivant.
25389  *
25390  * Fork - LGPL
25391  * <script type="text/javascript">
25392  */
25393
25394 /**
25395  * @class Roo.data.SimpleStore
25396  * @extends Roo.data.Store
25397  * Small helper class to make creating Stores from Array data easier.
25398  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25399  * @cfg {Array} fields An array of field definition objects, or field name strings.
25400  * @cfg {Object} an existing reader (eg. copied from another store)
25401  * @cfg {Array} data The multi-dimensional array of data
25402  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25403  * @cfg {Roo.data.Reader} reader  [not-required] 
25404  * @constructor
25405  * @param {Object} config
25406  */
25407 Roo.data.SimpleStore = function(config)
25408 {
25409     Roo.data.SimpleStore.superclass.constructor.call(this, {
25410         isLocal : true,
25411         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25412                 id: config.id
25413             },
25414             Roo.data.Record.create(config.fields)
25415         ),
25416         proxy : new Roo.data.MemoryProxy(config.data)
25417     });
25418     this.load();
25419 };
25420 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25421  * Based on:
25422  * Ext JS Library 1.1.1
25423  * Copyright(c) 2006-2007, Ext JS, LLC.
25424  *
25425  * Originally Released Under LGPL - original licence link has changed is not relivant.
25426  *
25427  * Fork - LGPL
25428  * <script type="text/javascript">
25429  */
25430
25431 /**
25432 /**
25433  * @extends Roo.data.Store
25434  * @class Roo.data.JsonStore
25435  * Small helper class to make creating Stores for JSON data easier. <br/>
25436 <pre><code>
25437 var store = new Roo.data.JsonStore({
25438     url: 'get-images.php',
25439     root: 'images',
25440     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25441 });
25442 </code></pre>
25443  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25444  * JsonReader and HttpProxy (unless inline data is provided).</b>
25445  * @cfg {Array} fields An array of field definition objects, or field name strings.
25446  * @constructor
25447  * @param {Object} config
25448  */
25449 Roo.data.JsonStore = function(c){
25450     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25451         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25452         reader: new Roo.data.JsonReader(c, c.fields)
25453     }));
25454 };
25455 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25456  * Based on:
25457  * Ext JS Library 1.1.1
25458  * Copyright(c) 2006-2007, Ext JS, LLC.
25459  *
25460  * Originally Released Under LGPL - original licence link has changed is not relivant.
25461  *
25462  * Fork - LGPL
25463  * <script type="text/javascript">
25464  */
25465
25466  
25467 Roo.data.Field = function(config){
25468     if(typeof config == "string"){
25469         config = {name: config};
25470     }
25471     Roo.apply(this, config);
25472     
25473     if(!this.type){
25474         this.type = "auto";
25475     }
25476     
25477     var st = Roo.data.SortTypes;
25478     // named sortTypes are supported, here we look them up
25479     if(typeof this.sortType == "string"){
25480         this.sortType = st[this.sortType];
25481     }
25482     
25483     // set default sortType for strings and dates
25484     if(!this.sortType){
25485         switch(this.type){
25486             case "string":
25487                 this.sortType = st.asUCString;
25488                 break;
25489             case "date":
25490                 this.sortType = st.asDate;
25491                 break;
25492             default:
25493                 this.sortType = st.none;
25494         }
25495     }
25496
25497     // define once
25498     var stripRe = /[\$,%]/g;
25499
25500     // prebuilt conversion function for this field, instead of
25501     // switching every time we're reading a value
25502     if(!this.convert){
25503         var cv, dateFormat = this.dateFormat;
25504         switch(this.type){
25505             case "":
25506             case "auto":
25507             case undefined:
25508                 cv = function(v){ return v; };
25509                 break;
25510             case "string":
25511                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25512                 break;
25513             case "int":
25514                 cv = function(v){
25515                     return v !== undefined && v !== null && v !== '' ?
25516                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25517                     };
25518                 break;
25519             case "float":
25520                 cv = function(v){
25521                     return v !== undefined && v !== null && v !== '' ?
25522                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25523                     };
25524                 break;
25525             case "bool":
25526             case "boolean":
25527                 cv = function(v){ return v === true || v === "true" || v == 1; };
25528                 break;
25529             case "date":
25530                 cv = function(v){
25531                     if(!v){
25532                         return '';
25533                     }
25534                     if(v instanceof Date){
25535                         return v;
25536                     }
25537                     if(dateFormat){
25538                         if(dateFormat == "timestamp"){
25539                             return new Date(v*1000);
25540                         }
25541                         return Date.parseDate(v, dateFormat);
25542                     }
25543                     var parsed = Date.parse(v);
25544                     return parsed ? new Date(parsed) : null;
25545                 };
25546              break;
25547             
25548         }
25549         this.convert = cv;
25550     }
25551 };
25552
25553 Roo.data.Field.prototype = {
25554     dateFormat: null,
25555     defaultValue: "",
25556     mapping: null,
25557     sortType : null,
25558     sortDir : "ASC"
25559 };/*
25560  * Based on:
25561  * Ext JS Library 1.1.1
25562  * Copyright(c) 2006-2007, Ext JS, LLC.
25563  *
25564  * Originally Released Under LGPL - original licence link has changed is not relivant.
25565  *
25566  * Fork - LGPL
25567  * <script type="text/javascript">
25568  */
25569  
25570 // Base class for reading structured data from a data source.  This class is intended to be
25571 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25572
25573 /**
25574  * @class Roo.data.DataReader
25575  * @abstract
25576  * Base class for reading structured data from a data source.  This class is intended to be
25577  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25578  */
25579
25580 Roo.data.DataReader = function(meta, recordType){
25581     
25582     this.meta = meta;
25583     
25584     this.recordType = recordType instanceof Array ? 
25585         Roo.data.Record.create(recordType) : recordType;
25586 };
25587
25588 Roo.data.DataReader.prototype = {
25589     
25590     
25591     readerType : 'Data',
25592      /**
25593      * Create an empty record
25594      * @param {Object} data (optional) - overlay some values
25595      * @return {Roo.data.Record} record created.
25596      */
25597     newRow :  function(d) {
25598         var da =  {};
25599         this.recordType.prototype.fields.each(function(c) {
25600             switch( c.type) {
25601                 case 'int' : da[c.name] = 0; break;
25602                 case 'date' : da[c.name] = new Date(); break;
25603                 case 'float' : da[c.name] = 0.0; break;
25604                 case 'boolean' : da[c.name] = false; break;
25605                 default : da[c.name] = ""; break;
25606             }
25607             
25608         });
25609         return new this.recordType(Roo.apply(da, d));
25610     }
25611     
25612     
25613 };/*
25614  * Based on:
25615  * Ext JS Library 1.1.1
25616  * Copyright(c) 2006-2007, Ext JS, LLC.
25617  *
25618  * Originally Released Under LGPL - original licence link has changed is not relivant.
25619  *
25620  * Fork - LGPL
25621  * <script type="text/javascript">
25622  */
25623
25624 /**
25625  * @class Roo.data.DataProxy
25626  * @extends Roo.util.Observable
25627  * @abstract
25628  * This class is an abstract base class for implementations which provide retrieval of
25629  * unformatted data objects.<br>
25630  * <p>
25631  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25632  * (of the appropriate type which knows how to parse the data object) to provide a block of
25633  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25634  * <p>
25635  * Custom implementations must implement the load method as described in
25636  * {@link Roo.data.HttpProxy#load}.
25637  */
25638 Roo.data.DataProxy = function(){
25639     this.addEvents({
25640         /**
25641          * @event beforeload
25642          * Fires before a network request is made to retrieve a data object.
25643          * @param {Object} This DataProxy object.
25644          * @param {Object} params The params parameter to the load function.
25645          */
25646         beforeload : true,
25647         /**
25648          * @event load
25649          * Fires before the load method's callback is called.
25650          * @param {Object} This DataProxy object.
25651          * @param {Object} o The data object.
25652          * @param {Object} arg The callback argument object passed to the load function.
25653          */
25654         load : true,
25655         /**
25656          * @event loadexception
25657          * Fires if an Exception occurs during data retrieval.
25658          * @param {Object} This DataProxy object.
25659          * @param {Object} o The data object.
25660          * @param {Object} arg The callback argument object passed to the load function.
25661          * @param {Object} e The Exception.
25662          */
25663         loadexception : true
25664     });
25665     Roo.data.DataProxy.superclass.constructor.call(this);
25666 };
25667
25668 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25669
25670     /**
25671      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25672      */
25673 /*
25674  * Based on:
25675  * Ext JS Library 1.1.1
25676  * Copyright(c) 2006-2007, Ext JS, LLC.
25677  *
25678  * Originally Released Under LGPL - original licence link has changed is not relivant.
25679  *
25680  * Fork - LGPL
25681  * <script type="text/javascript">
25682  */
25683 /**
25684  * @class Roo.data.MemoryProxy
25685  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25686  * to the Reader when its load method is called.
25687  * @constructor
25688  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25689  */
25690 Roo.data.MemoryProxy = function(data){
25691     if (data.data) {
25692         data = data.data;
25693     }
25694     Roo.data.MemoryProxy.superclass.constructor.call(this);
25695     this.data = data;
25696 };
25697
25698 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25699     
25700     /**
25701      * Load data from the requested source (in this case an in-memory
25702      * data object passed to the constructor), read the data object into
25703      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25704      * process that block using the passed callback.
25705      * @param {Object} params This parameter is not used by the MemoryProxy class.
25706      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25707      * object into a block of Roo.data.Records.
25708      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25709      * The function must be passed <ul>
25710      * <li>The Record block object</li>
25711      * <li>The "arg" argument from the load function</li>
25712      * <li>A boolean success indicator</li>
25713      * </ul>
25714      * @param {Object} scope The scope in which to call the callback
25715      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25716      */
25717     load : function(params, reader, callback, scope, arg){
25718         params = params || {};
25719         var result;
25720         try {
25721             result = reader.readRecords(params.data ? params.data :this.data);
25722         }catch(e){
25723             this.fireEvent("loadexception", this, arg, null, e);
25724             callback.call(scope, null, arg, false);
25725             return;
25726         }
25727         callback.call(scope, result, arg, true);
25728     },
25729     
25730     // private
25731     update : function(params, records){
25732         
25733     }
25734 });/*
25735  * Based on:
25736  * Ext JS Library 1.1.1
25737  * Copyright(c) 2006-2007, Ext JS, LLC.
25738  *
25739  * Originally Released Under LGPL - original licence link has changed is not relivant.
25740  *
25741  * Fork - LGPL
25742  * <script type="text/javascript">
25743  */
25744 /**
25745  * @class Roo.data.HttpProxy
25746  * @extends Roo.data.DataProxy
25747  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25748  * configured to reference a certain URL.<br><br>
25749  * <p>
25750  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25751  * from which the running page was served.<br><br>
25752  * <p>
25753  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25754  * <p>
25755  * Be aware that to enable the browser to parse an XML document, the server must set
25756  * the Content-Type header in the HTTP response to "text/xml".
25757  * @constructor
25758  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25759  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25760  * will be used to make the request.
25761  */
25762 Roo.data.HttpProxy = function(conn){
25763     Roo.data.HttpProxy.superclass.constructor.call(this);
25764     // is conn a conn config or a real conn?
25765     this.conn = conn;
25766     this.useAjax = !conn || !conn.events;
25767   
25768 };
25769
25770 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25771     // thse are take from connection...
25772     
25773     /**
25774      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25775      */
25776     /**
25777      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25778      * extra parameters to each request made by this object. (defaults to undefined)
25779      */
25780     /**
25781      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25782      *  to each request made by this object. (defaults to undefined)
25783      */
25784     /**
25785      * @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)
25786      */
25787     /**
25788      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25789      */
25790      /**
25791      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25792      * @type Boolean
25793      */
25794   
25795
25796     /**
25797      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25798      * @type Boolean
25799      */
25800     /**
25801      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25802      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25803      * a finer-grained basis than the DataProxy events.
25804      */
25805     getConnection : function(){
25806         return this.useAjax ? Roo.Ajax : this.conn;
25807     },
25808
25809     /**
25810      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25811      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25812      * process that block using the passed callback.
25813      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25814      * for the request to the remote server.
25815      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25816      * object into a block of Roo.data.Records.
25817      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25818      * The function must be passed <ul>
25819      * <li>The Record block object</li>
25820      * <li>The "arg" argument from the load function</li>
25821      * <li>A boolean success indicator</li>
25822      * </ul>
25823      * @param {Object} scope The scope in which to call the callback
25824      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25825      */
25826     load : function(params, reader, callback, scope, arg){
25827         if(this.fireEvent("beforeload", this, params) !== false){
25828             var  o = {
25829                 params : params || {},
25830                 request: {
25831                     callback : callback,
25832                     scope : scope,
25833                     arg : arg
25834                 },
25835                 reader: reader,
25836                 callback : this.loadResponse,
25837                 scope: this
25838             };
25839             if(this.useAjax){
25840                 Roo.applyIf(o, this.conn);
25841                 if(this.activeRequest){
25842                     Roo.Ajax.abort(this.activeRequest);
25843                 }
25844                 this.activeRequest = Roo.Ajax.request(o);
25845             }else{
25846                 this.conn.request(o);
25847             }
25848         }else{
25849             callback.call(scope||this, null, arg, false);
25850         }
25851     },
25852
25853     // private
25854     loadResponse : function(o, success, response){
25855         delete this.activeRequest;
25856         if(!success){
25857             this.fireEvent("loadexception", this, o, response);
25858             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25859             return;
25860         }
25861         var result;
25862         try {
25863             result = o.reader.read(response);
25864         }catch(e){
25865             o.success = false;
25866             o.raw = { errorMsg : response.responseText };
25867             this.fireEvent("loadexception", this, o, response, e);
25868             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25869             return;
25870         }
25871         
25872         this.fireEvent("load", this, o, o.request.arg);
25873         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25874     },
25875
25876     // private
25877     update : function(dataSet){
25878
25879     },
25880
25881     // private
25882     updateResponse : function(dataSet){
25883
25884     }
25885 });/*
25886  * Based on:
25887  * Ext JS Library 1.1.1
25888  * Copyright(c) 2006-2007, Ext JS, LLC.
25889  *
25890  * Originally Released Under LGPL - original licence link has changed is not relivant.
25891  *
25892  * Fork - LGPL
25893  * <script type="text/javascript">
25894  */
25895
25896 /**
25897  * @class Roo.data.ScriptTagProxy
25898  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25899  * other than the originating domain of the running page.<br><br>
25900  * <p>
25901  * <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
25902  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25903  * <p>
25904  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25905  * source code that is used as the source inside a &lt;script> tag.<br><br>
25906  * <p>
25907  * In order for the browser to process the returned data, the server must wrap the data object
25908  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25909  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25910  * depending on whether the callback name was passed:
25911  * <p>
25912  * <pre><code>
25913 boolean scriptTag = false;
25914 String cb = request.getParameter("callback");
25915 if (cb != null) {
25916     scriptTag = true;
25917     response.setContentType("text/javascript");
25918 } else {
25919     response.setContentType("application/x-json");
25920 }
25921 Writer out = response.getWriter();
25922 if (scriptTag) {
25923     out.write(cb + "(");
25924 }
25925 out.print(dataBlock.toJsonString());
25926 if (scriptTag) {
25927     out.write(");");
25928 }
25929 </pre></code>
25930  *
25931  * @constructor
25932  * @param {Object} config A configuration object.
25933  */
25934 Roo.data.ScriptTagProxy = function(config){
25935     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25936     Roo.apply(this, config);
25937     this.head = document.getElementsByTagName("head")[0];
25938 };
25939
25940 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25941
25942 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25943     /**
25944      * @cfg {String} url The URL from which to request the data object.
25945      */
25946     /**
25947      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25948      */
25949     timeout : 30000,
25950     /**
25951      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25952      * the server the name of the callback function set up by the load call to process the returned data object.
25953      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25954      * javascript output which calls this named function passing the data object as its only parameter.
25955      */
25956     callbackParam : "callback",
25957     /**
25958      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25959      * name to the request.
25960      */
25961     nocache : true,
25962
25963     /**
25964      * Load data from the configured URL, read the data object into
25965      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25966      * process that block using the passed callback.
25967      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25968      * for the request to the remote server.
25969      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25970      * object into a block of Roo.data.Records.
25971      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25972      * The function must be passed <ul>
25973      * <li>The Record block object</li>
25974      * <li>The "arg" argument from the load function</li>
25975      * <li>A boolean success indicator</li>
25976      * </ul>
25977      * @param {Object} scope The scope in which to call the callback
25978      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25979      */
25980     load : function(params, reader, callback, scope, arg){
25981         if(this.fireEvent("beforeload", this, params) !== false){
25982
25983             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25984
25985             var url = this.url;
25986             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25987             if(this.nocache){
25988                 url += "&_dc=" + (new Date().getTime());
25989             }
25990             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25991             var trans = {
25992                 id : transId,
25993                 cb : "stcCallback"+transId,
25994                 scriptId : "stcScript"+transId,
25995                 params : params,
25996                 arg : arg,
25997                 url : url,
25998                 callback : callback,
25999                 scope : scope,
26000                 reader : reader
26001             };
26002             var conn = this;
26003
26004             window[trans.cb] = function(o){
26005                 conn.handleResponse(o, trans);
26006             };
26007
26008             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
26009
26010             if(this.autoAbort !== false){
26011                 this.abort();
26012             }
26013
26014             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
26015
26016             var script = document.createElement("script");
26017             script.setAttribute("src", url);
26018             script.setAttribute("type", "text/javascript");
26019             script.setAttribute("id", trans.scriptId);
26020             this.head.appendChild(script);
26021
26022             this.trans = trans;
26023         }else{
26024             callback.call(scope||this, null, arg, false);
26025         }
26026     },
26027
26028     // private
26029     isLoading : function(){
26030         return this.trans ? true : false;
26031     },
26032
26033     /**
26034      * Abort the current server request.
26035      */
26036     abort : function(){
26037         if(this.isLoading()){
26038             this.destroyTrans(this.trans);
26039         }
26040     },
26041
26042     // private
26043     destroyTrans : function(trans, isLoaded){
26044         this.head.removeChild(document.getElementById(trans.scriptId));
26045         clearTimeout(trans.timeoutId);
26046         if(isLoaded){
26047             window[trans.cb] = undefined;
26048             try{
26049                 delete window[trans.cb];
26050             }catch(e){}
26051         }else{
26052             // if hasn't been loaded, wait for load to remove it to prevent script error
26053             window[trans.cb] = function(){
26054                 window[trans.cb] = undefined;
26055                 try{
26056                     delete window[trans.cb];
26057                 }catch(e){}
26058             };
26059         }
26060     },
26061
26062     // private
26063     handleResponse : function(o, trans){
26064         this.trans = false;
26065         this.destroyTrans(trans, true);
26066         var result;
26067         try {
26068             result = trans.reader.readRecords(o);
26069         }catch(e){
26070             this.fireEvent("loadexception", this, o, trans.arg, e);
26071             trans.callback.call(trans.scope||window, null, trans.arg, false);
26072             return;
26073         }
26074         this.fireEvent("load", this, o, trans.arg);
26075         trans.callback.call(trans.scope||window, result, trans.arg, true);
26076     },
26077
26078     // private
26079     handleFailure : function(trans){
26080         this.trans = false;
26081         this.destroyTrans(trans, false);
26082         this.fireEvent("loadexception", this, null, trans.arg);
26083         trans.callback.call(trans.scope||window, null, trans.arg, false);
26084     }
26085 });/*
26086  * Based on:
26087  * Ext JS Library 1.1.1
26088  * Copyright(c) 2006-2007, Ext JS, LLC.
26089  *
26090  * Originally Released Under LGPL - original licence link has changed is not relivant.
26091  *
26092  * Fork - LGPL
26093  * <script type="text/javascript">
26094  */
26095
26096 /**
26097  * @class Roo.data.JsonReader
26098  * @extends Roo.data.DataReader
26099  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
26100  * based on mappings in a provided Roo.data.Record constructor.
26101  * 
26102  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
26103  * in the reply previously. 
26104  * 
26105  * <p>
26106  * Example code:
26107  * <pre><code>
26108 var RecordDef = Roo.data.Record.create([
26109     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26110     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26111 ]);
26112 var myReader = new Roo.data.JsonReader({
26113     totalProperty: "results",    // The property which contains the total dataset size (optional)
26114     root: "rows",                // The property which contains an Array of row objects
26115     id: "id"                     // The property within each row object that provides an ID for the record (optional)
26116 }, RecordDef);
26117 </code></pre>
26118  * <p>
26119  * This would consume a JSON file like this:
26120  * <pre><code>
26121 { 'results': 2, 'rows': [
26122     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
26123     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
26124 }
26125 </code></pre>
26126  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
26127  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26128  * paged from the remote server.
26129  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
26130  * @cfg {String} root name of the property which contains the Array of row objects.
26131  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26132  * @cfg {Array} fields Array of field definition objects
26133  * @constructor
26134  * Create a new JsonReader
26135  * @param {Object} meta Metadata configuration options
26136  * @param {Object} recordType Either an Array of field definition objects,
26137  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
26138  */
26139 Roo.data.JsonReader = function(meta, recordType){
26140     
26141     meta = meta || {};
26142     // set some defaults:
26143     Roo.applyIf(meta, {
26144         totalProperty: 'total',
26145         successProperty : 'success',
26146         root : 'data',
26147         id : 'id'
26148     });
26149     
26150     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26151 };
26152 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
26153     
26154     readerType : 'Json',
26155     
26156     /**
26157      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
26158      * Used by Store query builder to append _requestMeta to params.
26159      * 
26160      */
26161     metaFromRemote : false,
26162     /**
26163      * This method is only used by a DataProxy which has retrieved data from a remote server.
26164      * @param {Object} response The XHR object which contains the JSON data in its responseText.
26165      * @return {Object} data A data block which is used by an Roo.data.Store object as
26166      * a cache of Roo.data.Records.
26167      */
26168     read : function(response){
26169         var json = response.responseText;
26170        
26171         var o = /* eval:var:o */ eval("("+json+")");
26172         if(!o) {
26173             throw {message: "JsonReader.read: Json object not found"};
26174         }
26175         
26176         if(o.metaData){
26177             
26178             delete this.ef;
26179             this.metaFromRemote = true;
26180             this.meta = o.metaData;
26181             this.recordType = Roo.data.Record.create(o.metaData.fields);
26182             this.onMetaChange(this.meta, this.recordType, o);
26183         }
26184         return this.readRecords(o);
26185     },
26186
26187     // private function a store will implement
26188     onMetaChange : function(meta, recordType, o){
26189
26190     },
26191
26192     /**
26193          * @ignore
26194          */
26195     simpleAccess: function(obj, subsc) {
26196         return obj[subsc];
26197     },
26198
26199         /**
26200          * @ignore
26201          */
26202     getJsonAccessor: function(){
26203         var re = /[\[\.]/;
26204         return function(expr) {
26205             try {
26206                 return(re.test(expr))
26207                     ? new Function("obj", "return obj." + expr)
26208                     : function(obj){
26209                         return obj[expr];
26210                     };
26211             } catch(e){}
26212             return Roo.emptyFn;
26213         };
26214     }(),
26215
26216     /**
26217      * Create a data block containing Roo.data.Records from an XML document.
26218      * @param {Object} o An object which contains an Array of row objects in the property specified
26219      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
26220      * which contains the total size of the dataset.
26221      * @return {Object} data A data block which is used by an Roo.data.Store object as
26222      * a cache of Roo.data.Records.
26223      */
26224     readRecords : function(o){
26225         /**
26226          * After any data loads, the raw JSON data is available for further custom processing.
26227          * @type Object
26228          */
26229         this.o = o;
26230         var s = this.meta, Record = this.recordType,
26231             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
26232
26233 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
26234         if (!this.ef) {
26235             if(s.totalProperty) {
26236                     this.getTotal = this.getJsonAccessor(s.totalProperty);
26237                 }
26238                 if(s.successProperty) {
26239                     this.getSuccess = this.getJsonAccessor(s.successProperty);
26240                 }
26241                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
26242                 if (s.id) {
26243                         var g = this.getJsonAccessor(s.id);
26244                         this.getId = function(rec) {
26245                                 var r = g(rec);  
26246                                 return (r === undefined || r === "") ? null : r;
26247                         };
26248                 } else {
26249                         this.getId = function(){return null;};
26250                 }
26251             this.ef = [];
26252             for(var jj = 0; jj < fl; jj++){
26253                 f = fi[jj];
26254                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
26255                 this.ef[jj] = this.getJsonAccessor(map);
26256             }
26257         }
26258
26259         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
26260         if(s.totalProperty){
26261             var vt = parseInt(this.getTotal(o), 10);
26262             if(!isNaN(vt)){
26263                 totalRecords = vt;
26264             }
26265         }
26266         if(s.successProperty){
26267             var vs = this.getSuccess(o);
26268             if(vs === false || vs === 'false'){
26269                 success = false;
26270             }
26271         }
26272         var records = [];
26273         for(var i = 0; i < c; i++){
26274             var n = root[i];
26275             var values = {};
26276             var id = this.getId(n);
26277             for(var j = 0; j < fl; j++){
26278                 f = fi[j];
26279                                 var v = this.ef[j](n);
26280                                 if (!f.convert) {
26281                                         Roo.log('missing convert for ' + f.name);
26282                                         Roo.log(f);
26283                                         continue;
26284                                 }
26285                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
26286             }
26287                         if (!Record) {
26288                                 return {
26289                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
26290                                         success : false,
26291                                         records : [],
26292                                         totalRecords : 0
26293                                 };
26294                         }
26295             var record = new Record(values, id);
26296             record.json = n;
26297             records[i] = record;
26298         }
26299         return {
26300             raw : o,
26301             success : success,
26302             records : records,
26303             totalRecords : totalRecords
26304         };
26305     },
26306     // used when loading children.. @see loadDataFromChildren
26307     toLoadData: function(rec)
26308     {
26309         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26310         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26311         return { data : data, total : data.length };
26312         
26313     }
26314 });/*
26315  * Based on:
26316  * Ext JS Library 1.1.1
26317  * Copyright(c) 2006-2007, Ext JS, LLC.
26318  *
26319  * Originally Released Under LGPL - original licence link has changed is not relivant.
26320  *
26321  * Fork - LGPL
26322  * <script type="text/javascript">
26323  */
26324
26325 /**
26326  * @class Roo.data.XmlReader
26327  * @extends Roo.data.DataReader
26328  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
26329  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26330  * <p>
26331  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26332  * header in the HTTP response must be set to "text/xml".</em>
26333  * <p>
26334  * Example code:
26335  * <pre><code>
26336 var RecordDef = Roo.data.Record.create([
26337    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26338    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26339 ]);
26340 var myReader = new Roo.data.XmlReader({
26341    totalRecords: "results", // The element which contains the total dataset size (optional)
26342    record: "row",           // The repeated element which contains row information
26343    id: "id"                 // The element within the row that provides an ID for the record (optional)
26344 }, RecordDef);
26345 </code></pre>
26346  * <p>
26347  * This would consume an XML file like this:
26348  * <pre><code>
26349 &lt;?xml?>
26350 &lt;dataset>
26351  &lt;results>2&lt;/results>
26352  &lt;row>
26353    &lt;id>1&lt;/id>
26354    &lt;name>Bill&lt;/name>
26355    &lt;occupation>Gardener&lt;/occupation>
26356  &lt;/row>
26357  &lt;row>
26358    &lt;id>2&lt;/id>
26359    &lt;name>Ben&lt;/name>
26360    &lt;occupation>Horticulturalist&lt;/occupation>
26361  &lt;/row>
26362 &lt;/dataset>
26363 </code></pre>
26364  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26365  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26366  * paged from the remote server.
26367  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26368  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26369  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26370  * a record identifier value.
26371  * @constructor
26372  * Create a new XmlReader
26373  * @param {Object} meta Metadata configuration options
26374  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26375  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26376  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26377  */
26378 Roo.data.XmlReader = function(meta, recordType){
26379     meta = meta || {};
26380     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26381 };
26382 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26383     
26384     readerType : 'Xml',
26385     
26386     /**
26387      * This method is only used by a DataProxy which has retrieved data from a remote server.
26388          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26389          * to contain a method called 'responseXML' that returns an XML document object.
26390      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26391      * a cache of Roo.data.Records.
26392      */
26393     read : function(response){
26394         var doc = response.responseXML;
26395         if(!doc) {
26396             throw {message: "XmlReader.read: XML Document not available"};
26397         }
26398         return this.readRecords(doc);
26399     },
26400
26401     /**
26402      * Create a data block containing Roo.data.Records from an XML document.
26403          * @param {Object} doc A parsed XML document.
26404      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26405      * a cache of Roo.data.Records.
26406      */
26407     readRecords : function(doc){
26408         /**
26409          * After any data loads/reads, the raw XML Document is available for further custom processing.
26410          * @type XMLDocument
26411          */
26412         this.xmlData = doc;
26413         var root = doc.documentElement || doc;
26414         var q = Roo.DomQuery;
26415         var recordType = this.recordType, fields = recordType.prototype.fields;
26416         var sid = this.meta.id;
26417         var totalRecords = 0, success = true;
26418         if(this.meta.totalRecords){
26419             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26420         }
26421         
26422         if(this.meta.success){
26423             var sv = q.selectValue(this.meta.success, root, true);
26424             success = sv !== false && sv !== 'false';
26425         }
26426         var records = [];
26427         var ns = q.select(this.meta.record, root);
26428         for(var i = 0, len = ns.length; i < len; i++) {
26429                 var n = ns[i];
26430                 var values = {};
26431                 var id = sid ? q.selectValue(sid, n) : undefined;
26432                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26433                     var f = fields.items[j];
26434                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26435                     v = f.convert(v);
26436                     values[f.name] = v;
26437                 }
26438                 var record = new recordType(values, id);
26439                 record.node = n;
26440                 records[records.length] = record;
26441             }
26442
26443             return {
26444                 success : success,
26445                 records : records,
26446                 totalRecords : totalRecords || records.length
26447             };
26448     }
26449 });/*
26450  * Based on:
26451  * Ext JS Library 1.1.1
26452  * Copyright(c) 2006-2007, Ext JS, LLC.
26453  *
26454  * Originally Released Under LGPL - original licence link has changed is not relivant.
26455  *
26456  * Fork - LGPL
26457  * <script type="text/javascript">
26458  */
26459
26460 /**
26461  * @class Roo.data.ArrayReader
26462  * @extends Roo.data.DataReader
26463  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26464  * Each element of that Array represents a row of data fields. The
26465  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26466  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26467  * <p>
26468  * Example code:.
26469  * <pre><code>
26470 var RecordDef = Roo.data.Record.create([
26471     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26472     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26473 ]);
26474 var myReader = new Roo.data.ArrayReader({
26475     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26476 }, RecordDef);
26477 </code></pre>
26478  * <p>
26479  * This would consume an Array like this:
26480  * <pre><code>
26481 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26482   </code></pre>
26483  
26484  * @constructor
26485  * Create a new JsonReader
26486  * @param {Object} meta Metadata configuration options.
26487  * @param {Object|Array} recordType Either an Array of field definition objects
26488  * 
26489  * @cfg {Array} fields Array of field definition objects
26490  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26491  * as specified to {@link Roo.data.Record#create},
26492  * or an {@link Roo.data.Record} object
26493  *
26494  * 
26495  * created using {@link Roo.data.Record#create}.
26496  */
26497 Roo.data.ArrayReader = function(meta, recordType)
26498 {    
26499     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26500 };
26501
26502 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26503     
26504       /**
26505      * Create a data block containing Roo.data.Records from an XML document.
26506      * @param {Object} o An Array of row objects which represents the dataset.
26507      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26508      * a cache of Roo.data.Records.
26509      */
26510     readRecords : function(o)
26511     {
26512         var sid = this.meta ? this.meta.id : null;
26513         var recordType = this.recordType, fields = recordType.prototype.fields;
26514         var records = [];
26515         var root = o;
26516         for(var i = 0; i < root.length; i++){
26517             var n = root[i];
26518             var values = {};
26519             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26520             for(var j = 0, jlen = fields.length; j < jlen; j++){
26521                 var f = fields.items[j];
26522                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26523                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26524                 v = f.convert(v);
26525                 values[f.name] = v;
26526             }
26527             var record = new recordType(values, id);
26528             record.json = n;
26529             records[records.length] = record;
26530         }
26531         return {
26532             records : records,
26533             totalRecords : records.length
26534         };
26535     },
26536     // used when loading children.. @see loadDataFromChildren
26537     toLoadData: function(rec)
26538     {
26539         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26540         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26541         
26542     }
26543     
26544     
26545 });/*
26546  * Based on:
26547  * Ext JS Library 1.1.1
26548  * Copyright(c) 2006-2007, Ext JS, LLC.
26549  *
26550  * Originally Released Under LGPL - original licence link has changed is not relivant.
26551  *
26552  * Fork - LGPL
26553  * <script type="text/javascript">
26554  */
26555
26556
26557 /**
26558  * @class Roo.data.Tree
26559  * @extends Roo.util.Observable
26560  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26561  * in the tree have most standard DOM functionality.
26562  * @constructor
26563  * @param {Node} root (optional) The root node
26564  */
26565 Roo.data.Tree = function(root){
26566    this.nodeHash = {};
26567    /**
26568     * The root node for this tree
26569     * @type Node
26570     */
26571    this.root = null;
26572    if(root){
26573        this.setRootNode(root);
26574    }
26575    this.addEvents({
26576        /**
26577         * @event append
26578         * Fires when a new child node is appended to a node in this tree.
26579         * @param {Tree} tree The owner tree
26580         * @param {Node} parent The parent node
26581         * @param {Node} node The newly appended node
26582         * @param {Number} index The index of the newly appended node
26583         */
26584        "append" : true,
26585        /**
26586         * @event remove
26587         * Fires when a child node is removed from a node in this tree.
26588         * @param {Tree} tree The owner tree
26589         * @param {Node} parent The parent node
26590         * @param {Node} node The child node removed
26591         */
26592        "remove" : true,
26593        /**
26594         * @event move
26595         * Fires when a node is moved to a new location in the tree
26596         * @param {Tree} tree The owner tree
26597         * @param {Node} node The node moved
26598         * @param {Node} oldParent The old parent of this node
26599         * @param {Node} newParent The new parent of this node
26600         * @param {Number} index The index it was moved to
26601         */
26602        "move" : true,
26603        /**
26604         * @event insert
26605         * Fires when a new child node is inserted in a node in this tree.
26606         * @param {Tree} tree The owner tree
26607         * @param {Node} parent The parent node
26608         * @param {Node} node The child node inserted
26609         * @param {Node} refNode The child node the node was inserted before
26610         */
26611        "insert" : true,
26612        /**
26613         * @event beforeappend
26614         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26615         * @param {Tree} tree The owner tree
26616         * @param {Node} parent The parent node
26617         * @param {Node} node The child node to be appended
26618         */
26619        "beforeappend" : true,
26620        /**
26621         * @event beforeremove
26622         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26623         * @param {Tree} tree The owner tree
26624         * @param {Node} parent The parent node
26625         * @param {Node} node The child node to be removed
26626         */
26627        "beforeremove" : true,
26628        /**
26629         * @event beforemove
26630         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26631         * @param {Tree} tree The owner tree
26632         * @param {Node} node The node being moved
26633         * @param {Node} oldParent The parent of the node
26634         * @param {Node} newParent The new parent the node is moving to
26635         * @param {Number} index The index it is being moved to
26636         */
26637        "beforemove" : true,
26638        /**
26639         * @event beforeinsert
26640         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26641         * @param {Tree} tree The owner tree
26642         * @param {Node} parent The parent node
26643         * @param {Node} node The child node to be inserted
26644         * @param {Node} refNode The child node the node is being inserted before
26645         */
26646        "beforeinsert" : true
26647    });
26648
26649     Roo.data.Tree.superclass.constructor.call(this);
26650 };
26651
26652 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26653     pathSeparator: "/",
26654
26655     proxyNodeEvent : function(){
26656         return this.fireEvent.apply(this, arguments);
26657     },
26658
26659     /**
26660      * Returns the root node for this tree.
26661      * @return {Node}
26662      */
26663     getRootNode : function(){
26664         return this.root;
26665     },
26666
26667     /**
26668      * Sets the root node for this tree.
26669      * @param {Node} node
26670      * @return {Node}
26671      */
26672     setRootNode : function(node){
26673         this.root = node;
26674         node.ownerTree = this;
26675         node.isRoot = true;
26676         this.registerNode(node);
26677         return node;
26678     },
26679
26680     /**
26681      * Gets a node in this tree by its id.
26682      * @param {String} id
26683      * @return {Node}
26684      */
26685     getNodeById : function(id){
26686         return this.nodeHash[id];
26687     },
26688
26689     registerNode : function(node){
26690         this.nodeHash[node.id] = node;
26691     },
26692
26693     unregisterNode : function(node){
26694         delete this.nodeHash[node.id];
26695     },
26696
26697     toString : function(){
26698         return "[Tree"+(this.id?" "+this.id:"")+"]";
26699     }
26700 });
26701
26702 /**
26703  * @class Roo.data.Node
26704  * @extends Roo.util.Observable
26705  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26706  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26707  * @constructor
26708  * @param {Object} attributes The attributes/config for the node
26709  */
26710 Roo.data.Node = function(attributes){
26711     /**
26712      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26713      * @type {Object}
26714      */
26715     this.attributes = attributes || {};
26716     this.leaf = this.attributes.leaf;
26717     /**
26718      * The node id. @type String
26719      */
26720     this.id = this.attributes.id;
26721     if(!this.id){
26722         this.id = Roo.id(null, "ynode-");
26723         this.attributes.id = this.id;
26724     }
26725      
26726     
26727     /**
26728      * All child nodes of this node. @type Array
26729      */
26730     this.childNodes = [];
26731     if(!this.childNodes.indexOf){ // indexOf is a must
26732         this.childNodes.indexOf = function(o){
26733             for(var i = 0, len = this.length; i < len; i++){
26734                 if(this[i] == o) {
26735                     return i;
26736                 }
26737             }
26738             return -1;
26739         };
26740     }
26741     /**
26742      * The parent node for this node. @type Node
26743      */
26744     this.parentNode = null;
26745     /**
26746      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26747      */
26748     this.firstChild = null;
26749     /**
26750      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26751      */
26752     this.lastChild = null;
26753     /**
26754      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26755      */
26756     this.previousSibling = null;
26757     /**
26758      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26759      */
26760     this.nextSibling = null;
26761
26762     this.addEvents({
26763        /**
26764         * @event append
26765         * Fires when a new child node is appended
26766         * @param {Tree} tree The owner tree
26767         * @param {Node} this This node
26768         * @param {Node} node The newly appended node
26769         * @param {Number} index The index of the newly appended node
26770         */
26771        "append" : true,
26772        /**
26773         * @event remove
26774         * Fires when a child node is removed
26775         * @param {Tree} tree The owner tree
26776         * @param {Node} this This node
26777         * @param {Node} node The removed node
26778         */
26779        "remove" : true,
26780        /**
26781         * @event move
26782         * Fires when this node is moved to a new location in the tree
26783         * @param {Tree} tree The owner tree
26784         * @param {Node} this This node
26785         * @param {Node} oldParent The old parent of this node
26786         * @param {Node} newParent The new parent of this node
26787         * @param {Number} index The index it was moved to
26788         */
26789        "move" : true,
26790        /**
26791         * @event insert
26792         * Fires when a new child node is inserted.
26793         * @param {Tree} tree The owner tree
26794         * @param {Node} this This node
26795         * @param {Node} node The child node inserted
26796         * @param {Node} refNode The child node the node was inserted before
26797         */
26798        "insert" : true,
26799        /**
26800         * @event beforeappend
26801         * Fires before a new child is appended, return false to cancel the append.
26802         * @param {Tree} tree The owner tree
26803         * @param {Node} this This node
26804         * @param {Node} node The child node to be appended
26805         */
26806        "beforeappend" : true,
26807        /**
26808         * @event beforeremove
26809         * Fires before a child is removed, return false to cancel the remove.
26810         * @param {Tree} tree The owner tree
26811         * @param {Node} this This node
26812         * @param {Node} node The child node to be removed
26813         */
26814        "beforeremove" : true,
26815        /**
26816         * @event beforemove
26817         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26818         * @param {Tree} tree The owner tree
26819         * @param {Node} this This node
26820         * @param {Node} oldParent The parent of this node
26821         * @param {Node} newParent The new parent this node is moving to
26822         * @param {Number} index The index it is being moved to
26823         */
26824        "beforemove" : true,
26825        /**
26826         * @event beforeinsert
26827         * Fires before a new child is inserted, return false to cancel the insert.
26828         * @param {Tree} tree The owner tree
26829         * @param {Node} this This node
26830         * @param {Node} node The child node to be inserted
26831         * @param {Node} refNode The child node the node is being inserted before
26832         */
26833        "beforeinsert" : true
26834    });
26835     this.listeners = this.attributes.listeners;
26836     Roo.data.Node.superclass.constructor.call(this);
26837 };
26838
26839 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26840     fireEvent : function(evtName){
26841         // first do standard event for this node
26842         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26843             return false;
26844         }
26845         // then bubble it up to the tree if the event wasn't cancelled
26846         var ot = this.getOwnerTree();
26847         if(ot){
26848             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26849                 return false;
26850             }
26851         }
26852         return true;
26853     },
26854
26855     /**
26856      * Returns true if this node is a leaf
26857      * @return {Boolean}
26858      */
26859     isLeaf : function(){
26860         return this.leaf === true;
26861     },
26862
26863     // private
26864     setFirstChild : function(node){
26865         this.firstChild = node;
26866     },
26867
26868     //private
26869     setLastChild : function(node){
26870         this.lastChild = node;
26871     },
26872
26873
26874     /**
26875      * Returns true if this node is the last child of its parent
26876      * @return {Boolean}
26877      */
26878     isLast : function(){
26879        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26880     },
26881
26882     /**
26883      * Returns true if this node is the first child of its parent
26884      * @return {Boolean}
26885      */
26886     isFirst : function(){
26887        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26888     },
26889
26890     hasChildNodes : function(){
26891         return !this.isLeaf() && this.childNodes.length > 0;
26892     },
26893
26894     /**
26895      * Insert node(s) as the last child node of this node.
26896      * @param {Node/Array} node The node or Array of nodes to append
26897      * @return {Node} The appended node if single append, or null if an array was passed
26898      */
26899     appendChild : function(node){
26900         var multi = false;
26901         if(node instanceof Array){
26902             multi = node;
26903         }else if(arguments.length > 1){
26904             multi = arguments;
26905         }
26906         
26907         // if passed an array or multiple args do them one by one
26908         if(multi){
26909             for(var i = 0, len = multi.length; i < len; i++) {
26910                 this.appendChild(multi[i]);
26911             }
26912         }else{
26913             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26914                 return false;
26915             }
26916             var index = this.childNodes.length;
26917             var oldParent = node.parentNode;
26918             // it's a move, make sure we move it cleanly
26919             if(oldParent){
26920                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26921                     return false;
26922                 }
26923                 oldParent.removeChild(node);
26924             }
26925             
26926             index = this.childNodes.length;
26927             if(index == 0){
26928                 this.setFirstChild(node);
26929             }
26930             this.childNodes.push(node);
26931             node.parentNode = this;
26932             var ps = this.childNodes[index-1];
26933             if(ps){
26934                 node.previousSibling = ps;
26935                 ps.nextSibling = node;
26936             }else{
26937                 node.previousSibling = null;
26938             }
26939             node.nextSibling = null;
26940             this.setLastChild(node);
26941             node.setOwnerTree(this.getOwnerTree());
26942             this.fireEvent("append", this.ownerTree, this, node, index);
26943             if(this.ownerTree) {
26944                 this.ownerTree.fireEvent("appendnode", this, node, index);
26945             }
26946             if(oldParent){
26947                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26948             }
26949             return node;
26950         }
26951     },
26952
26953     /**
26954      * Removes a child node from this node.
26955      * @param {Node} node The node to remove
26956      * @return {Node} The removed node
26957      */
26958     removeChild : function(node){
26959         var index = this.childNodes.indexOf(node);
26960         if(index == -1){
26961             return false;
26962         }
26963         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26964             return false;
26965         }
26966
26967         // remove it from childNodes collection
26968         this.childNodes.splice(index, 1);
26969
26970         // update siblings
26971         if(node.previousSibling){
26972             node.previousSibling.nextSibling = node.nextSibling;
26973         }
26974         if(node.nextSibling){
26975             node.nextSibling.previousSibling = node.previousSibling;
26976         }
26977
26978         // update child refs
26979         if(this.firstChild == node){
26980             this.setFirstChild(node.nextSibling);
26981         }
26982         if(this.lastChild == node){
26983             this.setLastChild(node.previousSibling);
26984         }
26985
26986         node.setOwnerTree(null);
26987         // clear any references from the node
26988         node.parentNode = null;
26989         node.previousSibling = null;
26990         node.nextSibling = null;
26991         this.fireEvent("remove", this.ownerTree, this, node);
26992         return node;
26993     },
26994
26995     /**
26996      * Inserts the first node before the second node in this nodes childNodes collection.
26997      * @param {Node} node The node to insert
26998      * @param {Node} refNode The node to insert before (if null the node is appended)
26999      * @return {Node} The inserted node
27000      */
27001     insertBefore : function(node, refNode){
27002         if(!refNode){ // like standard Dom, refNode can be null for append
27003             return this.appendChild(node);
27004         }
27005         // nothing to do
27006         if(node == refNode){
27007             return false;
27008         }
27009
27010         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
27011             return false;
27012         }
27013         var index = this.childNodes.indexOf(refNode);
27014         var oldParent = node.parentNode;
27015         var refIndex = index;
27016
27017         // when moving internally, indexes will change after remove
27018         if(oldParent == this && this.childNodes.indexOf(node) < index){
27019             refIndex--;
27020         }
27021
27022         // it's a move, make sure we move it cleanly
27023         if(oldParent){
27024             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
27025                 return false;
27026             }
27027             oldParent.removeChild(node);
27028         }
27029         if(refIndex == 0){
27030             this.setFirstChild(node);
27031         }
27032         this.childNodes.splice(refIndex, 0, node);
27033         node.parentNode = this;
27034         var ps = this.childNodes[refIndex-1];
27035         if(ps){
27036             node.previousSibling = ps;
27037             ps.nextSibling = node;
27038         }else{
27039             node.previousSibling = null;
27040         }
27041         node.nextSibling = refNode;
27042         refNode.previousSibling = node;
27043         node.setOwnerTree(this.getOwnerTree());
27044         this.fireEvent("insert", this.ownerTree, this, node, refNode);
27045         if(oldParent){
27046             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
27047         }
27048         return node;
27049     },
27050
27051     /**
27052      * Returns the child node at the specified index.
27053      * @param {Number} index
27054      * @return {Node}
27055      */
27056     item : function(index){
27057         return this.childNodes[index];
27058     },
27059
27060     /**
27061      * Replaces one child node in this node with another.
27062      * @param {Node} newChild The replacement node
27063      * @param {Node} oldChild The node to replace
27064      * @return {Node} The replaced node
27065      */
27066     replaceChild : function(newChild, oldChild){
27067         this.insertBefore(newChild, oldChild);
27068         this.removeChild(oldChild);
27069         return oldChild;
27070     },
27071
27072     /**
27073      * Returns the index of a child node
27074      * @param {Node} node
27075      * @return {Number} The index of the node or -1 if it was not found
27076      */
27077     indexOf : function(child){
27078         return this.childNodes.indexOf(child);
27079     },
27080
27081     /**
27082      * Returns the tree this node is in.
27083      * @return {Tree}
27084      */
27085     getOwnerTree : function(){
27086         // if it doesn't have one, look for one
27087         if(!this.ownerTree){
27088             var p = this;
27089             while(p){
27090                 if(p.ownerTree){
27091                     this.ownerTree = p.ownerTree;
27092                     break;
27093                 }
27094                 p = p.parentNode;
27095             }
27096         }
27097         return this.ownerTree;
27098     },
27099
27100     /**
27101      * Returns depth of this node (the root node has a depth of 0)
27102      * @return {Number}
27103      */
27104     getDepth : function(){
27105         var depth = 0;
27106         var p = this;
27107         while(p.parentNode){
27108             ++depth;
27109             p = p.parentNode;
27110         }
27111         return depth;
27112     },
27113
27114     // private
27115     setOwnerTree : function(tree){
27116         // if it's move, we need to update everyone
27117         if(tree != this.ownerTree){
27118             if(this.ownerTree){
27119                 this.ownerTree.unregisterNode(this);
27120             }
27121             this.ownerTree = tree;
27122             var cs = this.childNodes;
27123             for(var i = 0, len = cs.length; i < len; i++) {
27124                 cs[i].setOwnerTree(tree);
27125             }
27126             if(tree){
27127                 tree.registerNode(this);
27128             }
27129         }
27130     },
27131
27132     /**
27133      * Returns the path for this node. The path can be used to expand or select this node programmatically.
27134      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
27135      * @return {String} The path
27136      */
27137     getPath : function(attr){
27138         attr = attr || "id";
27139         var p = this.parentNode;
27140         var b = [this.attributes[attr]];
27141         while(p){
27142             b.unshift(p.attributes[attr]);
27143             p = p.parentNode;
27144         }
27145         var sep = this.getOwnerTree().pathSeparator;
27146         return sep + b.join(sep);
27147     },
27148
27149     /**
27150      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27151      * function call will be the scope provided or the current node. The arguments to the function
27152      * will be the args provided or the current node. If the function returns false at any point,
27153      * the bubble is stopped.
27154      * @param {Function} fn The function to call
27155      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27156      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27157      */
27158     bubble : function(fn, scope, args){
27159         var p = this;
27160         while(p){
27161             if(fn.call(scope || p, args || p) === false){
27162                 break;
27163             }
27164             p = p.parentNode;
27165         }
27166     },
27167
27168     /**
27169      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
27170      * function call will be the scope provided or the current node. The arguments to the function
27171      * will be the args provided or the current node. If the function returns false at any point,
27172      * the cascade is stopped on that branch.
27173      * @param {Function} fn The function to call
27174      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27175      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27176      */
27177     cascade : function(fn, scope, args){
27178         if(fn.call(scope || this, args || this) !== false){
27179             var cs = this.childNodes;
27180             for(var i = 0, len = cs.length; i < len; i++) {
27181                 cs[i].cascade(fn, scope, args);
27182             }
27183         }
27184     },
27185
27186     /**
27187      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
27188      * function call will be the scope provided or the current node. The arguments to the function
27189      * will be the args provided or the current node. If the function returns false at any point,
27190      * the iteration stops.
27191      * @param {Function} fn The function to call
27192      * @param {Object} scope (optional) The scope of the function (defaults to current node)
27193      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
27194      */
27195     eachChild : function(fn, scope, args){
27196         var cs = this.childNodes;
27197         for(var i = 0, len = cs.length; i < len; i++) {
27198                 if(fn.call(scope || this, args || cs[i]) === false){
27199                     break;
27200                 }
27201         }
27202     },
27203
27204     /**
27205      * Finds the first child that has the attribute with the specified value.
27206      * @param {String} attribute The attribute name
27207      * @param {Mixed} value The value to search for
27208      * @return {Node} The found child or null if none was found
27209      */
27210     findChild : function(attribute, value){
27211         var cs = this.childNodes;
27212         for(var i = 0, len = cs.length; i < len; i++) {
27213                 if(cs[i].attributes[attribute] == value){
27214                     return cs[i];
27215                 }
27216         }
27217         return null;
27218     },
27219
27220     /**
27221      * Finds the first child by a custom function. The child matches if the function passed
27222      * returns true.
27223      * @param {Function} fn
27224      * @param {Object} scope (optional)
27225      * @return {Node} The found child or null if none was found
27226      */
27227     findChildBy : function(fn, scope){
27228         var cs = this.childNodes;
27229         for(var i = 0, len = cs.length; i < len; i++) {
27230                 if(fn.call(scope||cs[i], cs[i]) === true){
27231                     return cs[i];
27232                 }
27233         }
27234         return null;
27235     },
27236
27237     /**
27238      * Sorts this nodes children using the supplied sort function
27239      * @param {Function} fn
27240      * @param {Object} scope (optional)
27241      */
27242     sort : function(fn, scope){
27243         var cs = this.childNodes;
27244         var len = cs.length;
27245         if(len > 0){
27246             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
27247             cs.sort(sortFn);
27248             for(var i = 0; i < len; i++){
27249                 var n = cs[i];
27250                 n.previousSibling = cs[i-1];
27251                 n.nextSibling = cs[i+1];
27252                 if(i == 0){
27253                     this.setFirstChild(n);
27254                 }
27255                 if(i == len-1){
27256                     this.setLastChild(n);
27257                 }
27258             }
27259         }
27260     },
27261
27262     /**
27263      * Returns true if this node is an ancestor (at any point) of the passed node.
27264      * @param {Node} node
27265      * @return {Boolean}
27266      */
27267     contains : function(node){
27268         return node.isAncestor(this);
27269     },
27270
27271     /**
27272      * Returns true if the passed node is an ancestor (at any point) of this node.
27273      * @param {Node} node
27274      * @return {Boolean}
27275      */
27276     isAncestor : function(node){
27277         var p = this.parentNode;
27278         while(p){
27279             if(p == node){
27280                 return true;
27281             }
27282             p = p.parentNode;
27283         }
27284         return false;
27285     },
27286
27287     toString : function(){
27288         return "[Node"+(this.id?" "+this.id:"")+"]";
27289     }
27290 });/*
27291  * Based on:
27292  * Ext JS Library 1.1.1
27293  * Copyright(c) 2006-2007, Ext JS, LLC.
27294  *
27295  * Originally Released Under LGPL - original licence link has changed is not relivant.
27296  *
27297  * Fork - LGPL
27298  * <script type="text/javascript">
27299  */
27300
27301
27302 /**
27303  * @class Roo.Shadow
27304  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
27305  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
27306  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
27307  * @constructor
27308  * Create a new Shadow
27309  * @param {Object} config The config object
27310  */
27311 Roo.Shadow = function(config){
27312     Roo.apply(this, config);
27313     if(typeof this.mode != "string"){
27314         this.mode = this.defaultMode;
27315     }
27316     var o = this.offset, a = {h: 0};
27317     var rad = Math.floor(this.offset/2);
27318     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
27319         case "drop":
27320             a.w = 0;
27321             a.l = a.t = o;
27322             a.t -= 1;
27323             if(Roo.isIE){
27324                 a.l -= this.offset + rad;
27325                 a.t -= this.offset + rad;
27326                 a.w -= rad;
27327                 a.h -= rad;
27328                 a.t += 1;
27329             }
27330         break;
27331         case "sides":
27332             a.w = (o*2);
27333             a.l = -o;
27334             a.t = o-1;
27335             if(Roo.isIE){
27336                 a.l -= (this.offset - rad);
27337                 a.t -= this.offset + rad;
27338                 a.l += 1;
27339                 a.w -= (this.offset - rad)*2;
27340                 a.w -= rad + 1;
27341                 a.h -= 1;
27342             }
27343         break;
27344         case "frame":
27345             a.w = a.h = (o*2);
27346             a.l = a.t = -o;
27347             a.t += 1;
27348             a.h -= 2;
27349             if(Roo.isIE){
27350                 a.l -= (this.offset - rad);
27351                 a.t -= (this.offset - rad);
27352                 a.l += 1;
27353                 a.w -= (this.offset + rad + 1);
27354                 a.h -= (this.offset + rad);
27355                 a.h += 1;
27356             }
27357         break;
27358     };
27359
27360     this.adjusts = a;
27361 };
27362
27363 Roo.Shadow.prototype = {
27364     /**
27365      * @cfg {String} mode
27366      * The shadow display mode.  Supports the following options:<br />
27367      * sides: Shadow displays on both sides and bottom only<br />
27368      * frame: Shadow displays equally on all four sides<br />
27369      * drop: Traditional bottom-right drop shadow (default)
27370      */
27371     mode: false,
27372     /**
27373      * @cfg {String} offset
27374      * The number of pixels to offset the shadow from the element (defaults to 4)
27375      */
27376     offset: 4,
27377
27378     // private
27379     defaultMode: "drop",
27380
27381     /**
27382      * Displays the shadow under the target element
27383      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27384      */
27385     show : function(target){
27386         target = Roo.get(target);
27387         if(!this.el){
27388             this.el = Roo.Shadow.Pool.pull();
27389             if(this.el.dom.nextSibling != target.dom){
27390                 this.el.insertBefore(target);
27391             }
27392         }
27393         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27394         if(Roo.isIE){
27395             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27396         }
27397         this.realign(
27398             target.getLeft(true),
27399             target.getTop(true),
27400             target.getWidth(),
27401             target.getHeight()
27402         );
27403         this.el.dom.style.display = "block";
27404     },
27405
27406     /**
27407      * Returns true if the shadow is visible, else false
27408      */
27409     isVisible : function(){
27410         return this.el ? true : false;  
27411     },
27412
27413     /**
27414      * Direct alignment when values are already available. Show must be called at least once before
27415      * calling this method to ensure it is initialized.
27416      * @param {Number} left The target element left position
27417      * @param {Number} top The target element top position
27418      * @param {Number} width The target element width
27419      * @param {Number} height The target element height
27420      */
27421     realign : function(l, t, w, h){
27422         if(!this.el){
27423             return;
27424         }
27425         var a = this.adjusts, d = this.el.dom, s = d.style;
27426         var iea = 0;
27427         s.left = (l+a.l)+"px";
27428         s.top = (t+a.t)+"px";
27429         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27430  
27431         if(s.width != sws || s.height != shs){
27432             s.width = sws;
27433             s.height = shs;
27434             if(!Roo.isIE){
27435                 var cn = d.childNodes;
27436                 var sww = Math.max(0, (sw-12))+"px";
27437                 cn[0].childNodes[1].style.width = sww;
27438                 cn[1].childNodes[1].style.width = sww;
27439                 cn[2].childNodes[1].style.width = sww;
27440                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27441             }
27442         }
27443     },
27444
27445     /**
27446      * Hides this shadow
27447      */
27448     hide : function(){
27449         if(this.el){
27450             this.el.dom.style.display = "none";
27451             Roo.Shadow.Pool.push(this.el);
27452             delete this.el;
27453         }
27454     },
27455
27456     /**
27457      * Adjust the z-index of this shadow
27458      * @param {Number} zindex The new z-index
27459      */
27460     setZIndex : function(z){
27461         this.zIndex = z;
27462         if(this.el){
27463             this.el.setStyle("z-index", z);
27464         }
27465     }
27466 };
27467
27468 // Private utility class that manages the internal Shadow cache
27469 Roo.Shadow.Pool = function(){
27470     var p = [];
27471     var markup = Roo.isIE ?
27472                  '<div class="x-ie-shadow"></div>' :
27473                  '<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>';
27474     return {
27475         pull : function(){
27476             var sh = p.shift();
27477             if(!sh){
27478                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27479                 sh.autoBoxAdjust = false;
27480             }
27481             return sh;
27482         },
27483
27484         push : function(sh){
27485             p.push(sh);
27486         }
27487     };
27488 }();/*
27489  * Based on:
27490  * Ext JS Library 1.1.1
27491  * Copyright(c) 2006-2007, Ext JS, LLC.
27492  *
27493  * Originally Released Under LGPL - original licence link has changed is not relivant.
27494  *
27495  * Fork - LGPL
27496  * <script type="text/javascript">
27497  */
27498
27499
27500 /**
27501  * @class Roo.SplitBar
27502  * @extends Roo.util.Observable
27503  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27504  * <br><br>
27505  * Usage:
27506  * <pre><code>
27507 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27508                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27509 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27510 split.minSize = 100;
27511 split.maxSize = 600;
27512 split.animate = true;
27513 split.on('moved', splitterMoved);
27514 </code></pre>
27515  * @constructor
27516  * Create a new SplitBar
27517  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27518  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27519  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27520  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27521                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27522                         position of the SplitBar).
27523  */
27524 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27525     
27526     /** @private */
27527     this.el = Roo.get(dragElement, true);
27528     this.el.dom.unselectable = "on";
27529     /** @private */
27530     this.resizingEl = Roo.get(resizingElement, true);
27531
27532     /**
27533      * @private
27534      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27535      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27536      * @type Number
27537      */
27538     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27539     
27540     /**
27541      * The minimum size of the resizing element. (Defaults to 0)
27542      * @type Number
27543      */
27544     this.minSize = 0;
27545     
27546     /**
27547      * The maximum size of the resizing element. (Defaults to 2000)
27548      * @type Number
27549      */
27550     this.maxSize = 2000;
27551     
27552     /**
27553      * Whether to animate the transition to the new size
27554      * @type Boolean
27555      */
27556     this.animate = false;
27557     
27558     /**
27559      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27560      * @type Boolean
27561      */
27562     this.useShim = false;
27563     
27564     /** @private */
27565     this.shim = null;
27566     
27567     if(!existingProxy){
27568         /** @private */
27569         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27570     }else{
27571         this.proxy = Roo.get(existingProxy).dom;
27572     }
27573     /** @private */
27574     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27575     
27576     /** @private */
27577     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27578     
27579     /** @private */
27580     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27581     
27582     /** @private */
27583     this.dragSpecs = {};
27584     
27585     /**
27586      * @private The adapter to use to positon and resize elements
27587      */
27588     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27589     this.adapter.init(this);
27590     
27591     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27592         /** @private */
27593         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27594         this.el.addClass("x-splitbar-h");
27595     }else{
27596         /** @private */
27597         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27598         this.el.addClass("x-splitbar-v");
27599     }
27600     
27601     this.addEvents({
27602         /**
27603          * @event resize
27604          * Fires when the splitter is moved (alias for {@link #event-moved})
27605          * @param {Roo.SplitBar} this
27606          * @param {Number} newSize the new width or height
27607          */
27608         "resize" : true,
27609         /**
27610          * @event moved
27611          * Fires when the splitter is moved
27612          * @param {Roo.SplitBar} this
27613          * @param {Number} newSize the new width or height
27614          */
27615         "moved" : true,
27616         /**
27617          * @event beforeresize
27618          * Fires before the splitter is dragged
27619          * @param {Roo.SplitBar} this
27620          */
27621         "beforeresize" : true,
27622
27623         "beforeapply" : true
27624     });
27625
27626     Roo.util.Observable.call(this);
27627 };
27628
27629 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27630     onStartProxyDrag : function(x, y){
27631         this.fireEvent("beforeresize", this);
27632         if(!this.overlay){
27633             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27634             o.unselectable();
27635             o.enableDisplayMode("block");
27636             // all splitbars share the same overlay
27637             Roo.SplitBar.prototype.overlay = o;
27638         }
27639         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27640         this.overlay.show();
27641         Roo.get(this.proxy).setDisplayed("block");
27642         var size = this.adapter.getElementSize(this);
27643         this.activeMinSize = this.getMinimumSize();;
27644         this.activeMaxSize = this.getMaximumSize();;
27645         var c1 = size - this.activeMinSize;
27646         var c2 = Math.max(this.activeMaxSize - size, 0);
27647         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27648             this.dd.resetConstraints();
27649             this.dd.setXConstraint(
27650                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27651                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27652             );
27653             this.dd.setYConstraint(0, 0);
27654         }else{
27655             this.dd.resetConstraints();
27656             this.dd.setXConstraint(0, 0);
27657             this.dd.setYConstraint(
27658                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27659                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27660             );
27661          }
27662         this.dragSpecs.startSize = size;
27663         this.dragSpecs.startPoint = [x, y];
27664         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27665     },
27666     
27667     /** 
27668      * @private Called after the drag operation by the DDProxy
27669      */
27670     onEndProxyDrag : function(e){
27671         Roo.get(this.proxy).setDisplayed(false);
27672         var endPoint = Roo.lib.Event.getXY(e);
27673         if(this.overlay){
27674             this.overlay.hide();
27675         }
27676         var newSize;
27677         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27678             newSize = this.dragSpecs.startSize + 
27679                 (this.placement == Roo.SplitBar.LEFT ?
27680                     endPoint[0] - this.dragSpecs.startPoint[0] :
27681                     this.dragSpecs.startPoint[0] - endPoint[0]
27682                 );
27683         }else{
27684             newSize = this.dragSpecs.startSize + 
27685                 (this.placement == Roo.SplitBar.TOP ?
27686                     endPoint[1] - this.dragSpecs.startPoint[1] :
27687                     this.dragSpecs.startPoint[1] - endPoint[1]
27688                 );
27689         }
27690         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27691         if(newSize != this.dragSpecs.startSize){
27692             if(this.fireEvent('beforeapply', this, newSize) !== false){
27693                 this.adapter.setElementSize(this, newSize);
27694                 this.fireEvent("moved", this, newSize);
27695                 this.fireEvent("resize", this, newSize);
27696             }
27697         }
27698     },
27699     
27700     /**
27701      * Get the adapter this SplitBar uses
27702      * @return The adapter object
27703      */
27704     getAdapter : function(){
27705         return this.adapter;
27706     },
27707     
27708     /**
27709      * Set the adapter this SplitBar uses
27710      * @param {Object} adapter A SplitBar adapter object
27711      */
27712     setAdapter : function(adapter){
27713         this.adapter = adapter;
27714         this.adapter.init(this);
27715     },
27716     
27717     /**
27718      * Gets the minimum size for the resizing element
27719      * @return {Number} The minimum size
27720      */
27721     getMinimumSize : function(){
27722         return this.minSize;
27723     },
27724     
27725     /**
27726      * Sets the minimum size for the resizing element
27727      * @param {Number} minSize The minimum size
27728      */
27729     setMinimumSize : function(minSize){
27730         this.minSize = minSize;
27731     },
27732     
27733     /**
27734      * Gets the maximum size for the resizing element
27735      * @return {Number} The maximum size
27736      */
27737     getMaximumSize : function(){
27738         return this.maxSize;
27739     },
27740     
27741     /**
27742      * Sets the maximum size for the resizing element
27743      * @param {Number} maxSize The maximum size
27744      */
27745     setMaximumSize : function(maxSize){
27746         this.maxSize = maxSize;
27747     },
27748     
27749     /**
27750      * Sets the initialize size for the resizing element
27751      * @param {Number} size The initial size
27752      */
27753     setCurrentSize : function(size){
27754         var oldAnimate = this.animate;
27755         this.animate = false;
27756         this.adapter.setElementSize(this, size);
27757         this.animate = oldAnimate;
27758     },
27759     
27760     /**
27761      * Destroy this splitbar. 
27762      * @param {Boolean} removeEl True to remove the element
27763      */
27764     destroy : function(removeEl){
27765         if(this.shim){
27766             this.shim.remove();
27767         }
27768         this.dd.unreg();
27769         this.proxy.parentNode.removeChild(this.proxy);
27770         if(removeEl){
27771             this.el.remove();
27772         }
27773     }
27774 });
27775
27776 /**
27777  * @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.
27778  */
27779 Roo.SplitBar.createProxy = function(dir){
27780     var proxy = new Roo.Element(document.createElement("div"));
27781     proxy.unselectable();
27782     var cls = 'x-splitbar-proxy';
27783     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27784     document.body.appendChild(proxy.dom);
27785     return proxy.dom;
27786 };
27787
27788 /** 
27789  * @class Roo.SplitBar.BasicLayoutAdapter
27790  * Default Adapter. It assumes the splitter and resizing element are not positioned
27791  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27792  */
27793 Roo.SplitBar.BasicLayoutAdapter = function(){
27794 };
27795
27796 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27797     // do nothing for now
27798     init : function(s){
27799     
27800     },
27801     /**
27802      * Called before drag operations to get the current size of the resizing element. 
27803      * @param {Roo.SplitBar} s The SplitBar using this adapter
27804      */
27805      getElementSize : function(s){
27806         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27807             return s.resizingEl.getWidth();
27808         }else{
27809             return s.resizingEl.getHeight();
27810         }
27811     },
27812     
27813     /**
27814      * Called after drag operations to set the size of the resizing element.
27815      * @param {Roo.SplitBar} s The SplitBar using this adapter
27816      * @param {Number} newSize The new size to set
27817      * @param {Function} onComplete A function to be invoked when resizing is complete
27818      */
27819     setElementSize : function(s, newSize, onComplete){
27820         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27821             if(!s.animate){
27822                 s.resizingEl.setWidth(newSize);
27823                 if(onComplete){
27824                     onComplete(s, newSize);
27825                 }
27826             }else{
27827                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27828             }
27829         }else{
27830             
27831             if(!s.animate){
27832                 s.resizingEl.setHeight(newSize);
27833                 if(onComplete){
27834                     onComplete(s, newSize);
27835                 }
27836             }else{
27837                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27838             }
27839         }
27840     }
27841 };
27842
27843 /** 
27844  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27845  * @extends Roo.SplitBar.BasicLayoutAdapter
27846  * Adapter that  moves the splitter element to align with the resized sizing element. 
27847  * Used with an absolute positioned SplitBar.
27848  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27849  * document.body, make sure you assign an id to the body element.
27850  */
27851 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27852     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27853     this.container = Roo.get(container);
27854 };
27855
27856 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27857     init : function(s){
27858         this.basic.init(s);
27859     },
27860     
27861     getElementSize : function(s){
27862         return this.basic.getElementSize(s);
27863     },
27864     
27865     setElementSize : function(s, newSize, onComplete){
27866         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27867     },
27868     
27869     moveSplitter : function(s){
27870         var yes = Roo.SplitBar;
27871         switch(s.placement){
27872             case yes.LEFT:
27873                 s.el.setX(s.resizingEl.getRight());
27874                 break;
27875             case yes.RIGHT:
27876                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27877                 break;
27878             case yes.TOP:
27879                 s.el.setY(s.resizingEl.getBottom());
27880                 break;
27881             case yes.BOTTOM:
27882                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27883                 break;
27884         }
27885     }
27886 };
27887
27888 /**
27889  * Orientation constant - Create a vertical SplitBar
27890  * @static
27891  * @type Number
27892  */
27893 Roo.SplitBar.VERTICAL = 1;
27894
27895 /**
27896  * Orientation constant - Create a horizontal SplitBar
27897  * @static
27898  * @type Number
27899  */
27900 Roo.SplitBar.HORIZONTAL = 2;
27901
27902 /**
27903  * Placement constant - The resizing element is to the left of the splitter element
27904  * @static
27905  * @type Number
27906  */
27907 Roo.SplitBar.LEFT = 1;
27908
27909 /**
27910  * Placement constant - The resizing element is to the right of the splitter element
27911  * @static
27912  * @type Number
27913  */
27914 Roo.SplitBar.RIGHT = 2;
27915
27916 /**
27917  * Placement constant - The resizing element is positioned above the splitter element
27918  * @static
27919  * @type Number
27920  */
27921 Roo.SplitBar.TOP = 3;
27922
27923 /**
27924  * Placement constant - The resizing element is positioned under splitter element
27925  * @static
27926  * @type Number
27927  */
27928 Roo.SplitBar.BOTTOM = 4;
27929 /*
27930  * Based on:
27931  * Ext JS Library 1.1.1
27932  * Copyright(c) 2006-2007, Ext JS, LLC.
27933  *
27934  * Originally Released Under LGPL - original licence link has changed is not relivant.
27935  *
27936  * Fork - LGPL
27937  * <script type="text/javascript">
27938  */
27939
27940 /**
27941  * @class Roo.View
27942  * @extends Roo.util.Observable
27943  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27944  * This class also supports single and multi selection modes. <br>
27945  * Create a data model bound view:
27946  <pre><code>
27947  var store = new Roo.data.Store(...);
27948
27949  var view = new Roo.View({
27950     el : "my-element",
27951     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27952  
27953     singleSelect: true,
27954     selectedClass: "ydataview-selected",
27955     store: store
27956  });
27957
27958  // listen for node click?
27959  view.on("click", function(vw, index, node, e){
27960  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27961  });
27962
27963  // load XML data
27964  dataModel.load("foobar.xml");
27965  </code></pre>
27966  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27967  * <br><br>
27968  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27969  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27970  * 
27971  * Note: old style constructor is still suported (container, template, config)
27972  * 
27973  * @constructor
27974  * Create a new View
27975  * @param {Object} config The config object
27976  * 
27977  */
27978 Roo.View = function(config, depreciated_tpl, depreciated_config){
27979     
27980     this.parent = false;
27981     
27982     if (typeof(depreciated_tpl) == 'undefined') {
27983         // new way.. - universal constructor.
27984         Roo.apply(this, config);
27985         this.el  = Roo.get(this.el);
27986     } else {
27987         // old format..
27988         this.el  = Roo.get(config);
27989         this.tpl = depreciated_tpl;
27990         Roo.apply(this, depreciated_config);
27991     }
27992     this.wrapEl  = this.el.wrap().wrap();
27993     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27994     
27995     
27996     if(typeof(this.tpl) == "string"){
27997         this.tpl = new Roo.Template(this.tpl);
27998     } else {
27999         // support xtype ctors..
28000         this.tpl = new Roo.factory(this.tpl, Roo);
28001     }
28002     
28003     
28004     this.tpl.compile();
28005     
28006     /** @private */
28007     this.addEvents({
28008         /**
28009          * @event beforeclick
28010          * Fires before a click is processed. Returns false to cancel the default action.
28011          * @param {Roo.View} this
28012          * @param {Number} index The index of the target node
28013          * @param {HTMLElement} node The target node
28014          * @param {Roo.EventObject} e The raw event object
28015          */
28016             "beforeclick" : true,
28017         /**
28018          * @event click
28019          * Fires when a template node is clicked.
28020          * @param {Roo.View} this
28021          * @param {Number} index The index of the target node
28022          * @param {HTMLElement} node The target node
28023          * @param {Roo.EventObject} e The raw event object
28024          */
28025             "click" : true,
28026         /**
28027          * @event dblclick
28028          * Fires when a template node is double clicked.
28029          * @param {Roo.View} this
28030          * @param {Number} index The index of the target node
28031          * @param {HTMLElement} node The target node
28032          * @param {Roo.EventObject} e The raw event object
28033          */
28034             "dblclick" : true,
28035         /**
28036          * @event contextmenu
28037          * Fires when a template node is right clicked.
28038          * @param {Roo.View} this
28039          * @param {Number} index The index of the target node
28040          * @param {HTMLElement} node The target node
28041          * @param {Roo.EventObject} e The raw event object
28042          */
28043             "contextmenu" : true,
28044         /**
28045          * @event selectionchange
28046          * Fires when the selected nodes change.
28047          * @param {Roo.View} this
28048          * @param {Array} selections Array of the selected nodes
28049          */
28050             "selectionchange" : true,
28051     
28052         /**
28053          * @event beforeselect
28054          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
28055          * @param {Roo.View} this
28056          * @param {HTMLElement} node The node to be selected
28057          * @param {Array} selections Array of currently selected nodes
28058          */
28059             "beforeselect" : true,
28060         /**
28061          * @event preparedata
28062          * Fires on every row to render, to allow you to change the data.
28063          * @param {Roo.View} this
28064          * @param {Object} data to be rendered (change this)
28065          */
28066           "preparedata" : true
28067           
28068           
28069         });
28070
28071
28072
28073     this.el.on({
28074         "click": this.onClick,
28075         "dblclick": this.onDblClick,
28076         "contextmenu": this.onContextMenu,
28077         scope:this
28078     });
28079
28080     this.selections = [];
28081     this.nodes = [];
28082     this.cmp = new Roo.CompositeElementLite([]);
28083     if(this.store){
28084         this.store = Roo.factory(this.store, Roo.data);
28085         this.setStore(this.store, true);
28086     }
28087     
28088     if ( this.footer && this.footer.xtype) {
28089            
28090          var fctr = this.wrapEl.appendChild(document.createElement("div"));
28091         
28092         this.footer.dataSource = this.store;
28093         this.footer.container = fctr;
28094         this.footer = Roo.factory(this.footer, Roo);
28095         fctr.insertFirst(this.el);
28096         
28097         // this is a bit insane - as the paging toolbar seems to detach the el..
28098 //        dom.parentNode.parentNode.parentNode
28099          // they get detached?
28100     }
28101     
28102     
28103     Roo.View.superclass.constructor.call(this);
28104     
28105     
28106 };
28107
28108 Roo.extend(Roo.View, Roo.util.Observable, {
28109     
28110      /**
28111      * @cfg {Roo.data.Store} store Data store to load data from.
28112      */
28113     store : false,
28114     
28115     /**
28116      * @cfg {String|Roo.Element} el The container element.
28117      */
28118     el : '',
28119     
28120     /**
28121      * @cfg {String|Roo.Template} tpl The template used by this View 
28122      */
28123     tpl : false,
28124     /**
28125      * @cfg {String} dataName the named area of the template to use as the data area
28126      *                          Works with domtemplates roo-name="name"
28127      */
28128     dataName: false,
28129     /**
28130      * @cfg {String} selectedClass The css class to add to selected nodes
28131      */
28132     selectedClass : "x-view-selected",
28133      /**
28134      * @cfg {String} emptyText The empty text to show when nothing is loaded.
28135      */
28136     emptyText : "",
28137     
28138     /**
28139      * @cfg {String} text to display on mask (default Loading)
28140      */
28141     mask : false,
28142     /**
28143      * @cfg {Boolean} multiSelect Allow multiple selection
28144      */
28145     multiSelect : false,
28146     /**
28147      * @cfg {Boolean} singleSelect Allow single selection
28148      */
28149     singleSelect:  false,
28150     
28151     /**
28152      * @cfg {Boolean} toggleSelect - selecting 
28153      */
28154     toggleSelect : false,
28155     
28156     /**
28157      * @cfg {Boolean} tickable - selecting 
28158      */
28159     tickable : false,
28160     
28161     /**
28162      * Returns the element this view is bound to.
28163      * @return {Roo.Element}
28164      */
28165     getEl : function(){
28166         return this.wrapEl;
28167     },
28168     
28169     
28170
28171     /**
28172      * Refreshes the view. - called by datachanged on the store. - do not call directly.
28173      */
28174     refresh : function(){
28175         //Roo.log('refresh');
28176         var t = this.tpl;
28177         
28178         // if we are using something like 'domtemplate', then
28179         // the what gets used is:
28180         // t.applySubtemplate(NAME, data, wrapping data..)
28181         // the outer template then get' applied with
28182         //     the store 'extra data'
28183         // and the body get's added to the
28184         //      roo-name="data" node?
28185         //      <span class='roo-tpl-{name}'></span> ?????
28186         
28187         
28188         
28189         this.clearSelections();
28190         this.el.update("");
28191         var html = [];
28192         var records = this.store.getRange();
28193         if(records.length < 1) {
28194             
28195             // is this valid??  = should it render a template??
28196             
28197             this.el.update(this.emptyText);
28198             return;
28199         }
28200         var el = this.el;
28201         if (this.dataName) {
28202             this.el.update(t.apply(this.store.meta)); //????
28203             el = this.el.child('.roo-tpl-' + this.dataName);
28204         }
28205         
28206         for(var i = 0, len = records.length; i < len; i++){
28207             var data = this.prepareData(records[i].data, i, records[i]);
28208             this.fireEvent("preparedata", this, data, i, records[i]);
28209             
28210             var d = Roo.apply({}, data);
28211             
28212             if(this.tickable){
28213                 Roo.apply(d, {'roo-id' : Roo.id()});
28214                 
28215                 var _this = this;
28216             
28217                 Roo.each(this.parent.item, function(item){
28218                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
28219                         return;
28220                     }
28221                     Roo.apply(d, {'roo-data-checked' : 'checked'});
28222                 });
28223             }
28224             
28225             html[html.length] = Roo.util.Format.trim(
28226                 this.dataName ?
28227                     t.applySubtemplate(this.dataName, d, this.store.meta) :
28228                     t.apply(d)
28229             );
28230         }
28231         
28232         
28233         
28234         el.update(html.join(""));
28235         this.nodes = el.dom.childNodes;
28236         this.updateIndexes(0);
28237     },
28238     
28239
28240     /**
28241      * Function to override to reformat the data that is sent to
28242      * the template for each node.
28243      * DEPRICATED - use the preparedata event handler.
28244      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
28245      * a JSON object for an UpdateManager bound view).
28246      */
28247     prepareData : function(data, index, record)
28248     {
28249         this.fireEvent("preparedata", this, data, index, record);
28250         return data;
28251     },
28252
28253     onUpdate : function(ds, record){
28254         // Roo.log('on update');   
28255         this.clearSelections();
28256         var index = this.store.indexOf(record);
28257         var n = this.nodes[index];
28258         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
28259         n.parentNode.removeChild(n);
28260         this.updateIndexes(index, index);
28261     },
28262
28263     
28264     
28265 // --------- FIXME     
28266     onAdd : function(ds, records, index)
28267     {
28268         //Roo.log(['on Add', ds, records, index] );        
28269         this.clearSelections();
28270         if(this.nodes.length == 0){
28271             this.refresh();
28272             return;
28273         }
28274         var n = this.nodes[index];
28275         for(var i = 0, len = records.length; i < len; i++){
28276             var d = this.prepareData(records[i].data, i, records[i]);
28277             if(n){
28278                 this.tpl.insertBefore(n, d);
28279             }else{
28280                 
28281                 this.tpl.append(this.el, d);
28282             }
28283         }
28284         this.updateIndexes(index);
28285     },
28286
28287     onRemove : function(ds, record, index){
28288        // Roo.log('onRemove');
28289         this.clearSelections();
28290         var el = this.dataName  ?
28291             this.el.child('.roo-tpl-' + this.dataName) :
28292             this.el; 
28293         
28294         el.dom.removeChild(this.nodes[index]);
28295         this.updateIndexes(index);
28296     },
28297
28298     /**
28299      * Refresh an individual node.
28300      * @param {Number} index
28301      */
28302     refreshNode : function(index){
28303         this.onUpdate(this.store, this.store.getAt(index));
28304     },
28305
28306     updateIndexes : function(startIndex, endIndex){
28307         var ns = this.nodes;
28308         startIndex = startIndex || 0;
28309         endIndex = endIndex || ns.length - 1;
28310         for(var i = startIndex; i <= endIndex; i++){
28311             ns[i].nodeIndex = i;
28312         }
28313     },
28314
28315     /**
28316      * Changes the data store this view uses and refresh the view.
28317      * @param {Store} store
28318      */
28319     setStore : function(store, initial){
28320         if(!initial && this.store){
28321             this.store.un("datachanged", this.refresh);
28322             this.store.un("add", this.onAdd);
28323             this.store.un("remove", this.onRemove);
28324             this.store.un("update", this.onUpdate);
28325             this.store.un("clear", this.refresh);
28326             this.store.un("beforeload", this.onBeforeLoad);
28327             this.store.un("load", this.onLoad);
28328             this.store.un("loadexception", this.onLoad);
28329         }
28330         if(store){
28331           
28332             store.on("datachanged", this.refresh, this);
28333             store.on("add", this.onAdd, this);
28334             store.on("remove", this.onRemove, this);
28335             store.on("update", this.onUpdate, this);
28336             store.on("clear", this.refresh, this);
28337             store.on("beforeload", this.onBeforeLoad, this);
28338             store.on("load", this.onLoad, this);
28339             store.on("loadexception", this.onLoad, this);
28340         }
28341         
28342         if(store){
28343             this.refresh();
28344         }
28345     },
28346     /**
28347      * onbeforeLoad - masks the loading area.
28348      *
28349      */
28350     onBeforeLoad : function(store,opts)
28351     {
28352          //Roo.log('onBeforeLoad');   
28353         if (!opts.add) {
28354             this.el.update("");
28355         }
28356         this.el.mask(this.mask ? this.mask : "Loading" ); 
28357     },
28358     onLoad : function ()
28359     {
28360         this.el.unmask();
28361     },
28362     
28363
28364     /**
28365      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28366      * @param {HTMLElement} node
28367      * @return {HTMLElement} The template node
28368      */
28369     findItemFromChild : function(node){
28370         var el = this.dataName  ?
28371             this.el.child('.roo-tpl-' + this.dataName,true) :
28372             this.el.dom; 
28373         
28374         if(!node || node.parentNode == el){
28375                     return node;
28376             }
28377             var p = node.parentNode;
28378             while(p && p != el){
28379             if(p.parentNode == el){
28380                 return p;
28381             }
28382             p = p.parentNode;
28383         }
28384             return null;
28385     },
28386
28387     /** @ignore */
28388     onClick : function(e){
28389         var item = this.findItemFromChild(e.getTarget());
28390         if(item){
28391             var index = this.indexOf(item);
28392             if(this.onItemClick(item, index, e) !== false){
28393                 this.fireEvent("click", this, index, item, e);
28394             }
28395         }else{
28396             this.clearSelections();
28397         }
28398     },
28399
28400     /** @ignore */
28401     onContextMenu : function(e){
28402         var item = this.findItemFromChild(e.getTarget());
28403         if(item){
28404             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28405         }
28406     },
28407
28408     /** @ignore */
28409     onDblClick : function(e){
28410         var item = this.findItemFromChild(e.getTarget());
28411         if(item){
28412             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28413         }
28414     },
28415
28416     onItemClick : function(item, index, e)
28417     {
28418         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28419             return false;
28420         }
28421         if (this.toggleSelect) {
28422             var m = this.isSelected(item) ? 'unselect' : 'select';
28423             //Roo.log(m);
28424             var _t = this;
28425             _t[m](item, true, false);
28426             return true;
28427         }
28428         if(this.multiSelect || this.singleSelect){
28429             if(this.multiSelect && e.shiftKey && this.lastSelection){
28430                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28431             }else{
28432                 this.select(item, this.multiSelect && e.ctrlKey);
28433                 this.lastSelection = item;
28434             }
28435             
28436             if(!this.tickable){
28437                 e.preventDefault();
28438             }
28439             
28440         }
28441         return true;
28442     },
28443
28444     /**
28445      * Get the number of selected nodes.
28446      * @return {Number}
28447      */
28448     getSelectionCount : function(){
28449         return this.selections.length;
28450     },
28451
28452     /**
28453      * Get the currently selected nodes.
28454      * @return {Array} An array of HTMLElements
28455      */
28456     getSelectedNodes : function(){
28457         return this.selections;
28458     },
28459
28460     /**
28461      * Get the indexes of the selected nodes.
28462      * @return {Array}
28463      */
28464     getSelectedIndexes : function(){
28465         var indexes = [], s = this.selections;
28466         for(var i = 0, len = s.length; i < len; i++){
28467             indexes.push(s[i].nodeIndex);
28468         }
28469         return indexes;
28470     },
28471
28472     /**
28473      * Clear all selections
28474      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28475      */
28476     clearSelections : function(suppressEvent){
28477         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28478             this.cmp.elements = this.selections;
28479             this.cmp.removeClass(this.selectedClass);
28480             this.selections = [];
28481             if(!suppressEvent){
28482                 this.fireEvent("selectionchange", this, this.selections);
28483             }
28484         }
28485     },
28486
28487     /**
28488      * Returns true if the passed node is selected
28489      * @param {HTMLElement/Number} node The node or node index
28490      * @return {Boolean}
28491      */
28492     isSelected : function(node){
28493         var s = this.selections;
28494         if(s.length < 1){
28495             return false;
28496         }
28497         node = this.getNode(node);
28498         return s.indexOf(node) !== -1;
28499     },
28500
28501     /**
28502      * Selects nodes.
28503      * @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
28504      * @param {Boolean} keepExisting (optional) true to keep existing selections
28505      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28506      */
28507     select : function(nodeInfo, keepExisting, suppressEvent){
28508         if(nodeInfo instanceof Array){
28509             if(!keepExisting){
28510                 this.clearSelections(true);
28511             }
28512             for(var i = 0, len = nodeInfo.length; i < len; i++){
28513                 this.select(nodeInfo[i], true, true);
28514             }
28515             return;
28516         } 
28517         var node = this.getNode(nodeInfo);
28518         if(!node || this.isSelected(node)){
28519             return; // already selected.
28520         }
28521         if(!keepExisting){
28522             this.clearSelections(true);
28523         }
28524         
28525         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28526             Roo.fly(node).addClass(this.selectedClass);
28527             this.selections.push(node);
28528             if(!suppressEvent){
28529                 this.fireEvent("selectionchange", this, this.selections);
28530             }
28531         }
28532         
28533         
28534     },
28535       /**
28536      * Unselects nodes.
28537      * @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
28538      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28539      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28540      */
28541     unselect : function(nodeInfo, keepExisting, suppressEvent)
28542     {
28543         if(nodeInfo instanceof Array){
28544             Roo.each(this.selections, function(s) {
28545                 this.unselect(s, nodeInfo);
28546             }, this);
28547             return;
28548         }
28549         var node = this.getNode(nodeInfo);
28550         if(!node || !this.isSelected(node)){
28551             //Roo.log("not selected");
28552             return; // not selected.
28553         }
28554         // fireevent???
28555         var ns = [];
28556         Roo.each(this.selections, function(s) {
28557             if (s == node ) {
28558                 Roo.fly(node).removeClass(this.selectedClass);
28559
28560                 return;
28561             }
28562             ns.push(s);
28563         },this);
28564         
28565         this.selections= ns;
28566         this.fireEvent("selectionchange", this, this.selections);
28567     },
28568
28569     /**
28570      * Gets a template node.
28571      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28572      * @return {HTMLElement} The node or null if it wasn't found
28573      */
28574     getNode : function(nodeInfo){
28575         if(typeof nodeInfo == "string"){
28576             return document.getElementById(nodeInfo);
28577         }else if(typeof nodeInfo == "number"){
28578             return this.nodes[nodeInfo];
28579         }
28580         return nodeInfo;
28581     },
28582
28583     /**
28584      * Gets a range template nodes.
28585      * @param {Number} startIndex
28586      * @param {Number} endIndex
28587      * @return {Array} An array of nodes
28588      */
28589     getNodes : function(start, end){
28590         var ns = this.nodes;
28591         start = start || 0;
28592         end = typeof end == "undefined" ? ns.length - 1 : end;
28593         var nodes = [];
28594         if(start <= end){
28595             for(var i = start; i <= end; i++){
28596                 nodes.push(ns[i]);
28597             }
28598         } else{
28599             for(var i = start; i >= end; i--){
28600                 nodes.push(ns[i]);
28601             }
28602         }
28603         return nodes;
28604     },
28605
28606     /**
28607      * Finds the index of the passed node
28608      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28609      * @return {Number} The index of the node or -1
28610      */
28611     indexOf : function(node){
28612         node = this.getNode(node);
28613         if(typeof node.nodeIndex == "number"){
28614             return node.nodeIndex;
28615         }
28616         var ns = this.nodes;
28617         for(var i = 0, len = ns.length; i < len; i++){
28618             if(ns[i] == node){
28619                 return i;
28620             }
28621         }
28622         return -1;
28623     }
28624 });
28625 /*
28626  * Based on:
28627  * Ext JS Library 1.1.1
28628  * Copyright(c) 2006-2007, Ext JS, LLC.
28629  *
28630  * Originally Released Under LGPL - original licence link has changed is not relivant.
28631  *
28632  * Fork - LGPL
28633  * <script type="text/javascript">
28634  */
28635
28636 /**
28637  * @class Roo.JsonView
28638  * @extends Roo.View
28639  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28640 <pre><code>
28641 var view = new Roo.JsonView({
28642     container: "my-element",
28643     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28644     multiSelect: true, 
28645     jsonRoot: "data" 
28646 });
28647
28648 // listen for node click?
28649 view.on("click", function(vw, index, node, e){
28650     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28651 });
28652
28653 // direct load of JSON data
28654 view.load("foobar.php");
28655
28656 // Example from my blog list
28657 var tpl = new Roo.Template(
28658     '&lt;div class="entry"&gt;' +
28659     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28660     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28661     "&lt;/div&gt;&lt;hr /&gt;"
28662 );
28663
28664 var moreView = new Roo.JsonView({
28665     container :  "entry-list", 
28666     template : tpl,
28667     jsonRoot: "posts"
28668 });
28669 moreView.on("beforerender", this.sortEntries, this);
28670 moreView.load({
28671     url: "/blog/get-posts.php",
28672     params: "allposts=true",
28673     text: "Loading Blog Entries..."
28674 });
28675 </code></pre>
28676
28677 * Note: old code is supported with arguments : (container, template, config)
28678
28679
28680  * @constructor
28681  * Create a new JsonView
28682  * 
28683  * @param {Object} config The config object
28684  * 
28685  */
28686 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28687     
28688     
28689     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28690
28691     var um = this.el.getUpdateManager();
28692     um.setRenderer(this);
28693     um.on("update", this.onLoad, this);
28694     um.on("failure", this.onLoadException, this);
28695
28696     /**
28697      * @event beforerender
28698      * Fires before rendering of the downloaded JSON data.
28699      * @param {Roo.JsonView} this
28700      * @param {Object} data The JSON data loaded
28701      */
28702     /**
28703      * @event load
28704      * Fires when data is loaded.
28705      * @param {Roo.JsonView} this
28706      * @param {Object} data The JSON data loaded
28707      * @param {Object} response The raw Connect response object
28708      */
28709     /**
28710      * @event loadexception
28711      * Fires when loading fails.
28712      * @param {Roo.JsonView} this
28713      * @param {Object} response The raw Connect response object
28714      */
28715     this.addEvents({
28716         'beforerender' : true,
28717         'load' : true,
28718         'loadexception' : true
28719     });
28720 };
28721 Roo.extend(Roo.JsonView, Roo.View, {
28722     /**
28723      * @type {String} The root property in the loaded JSON object that contains the data
28724      */
28725     jsonRoot : "",
28726
28727     /**
28728      * Refreshes the view.
28729      */
28730     refresh : function(){
28731         this.clearSelections();
28732         this.el.update("");
28733         var html = [];
28734         var o = this.jsonData;
28735         if(o && o.length > 0){
28736             for(var i = 0, len = o.length; i < len; i++){
28737                 var data = this.prepareData(o[i], i, o);
28738                 html[html.length] = this.tpl.apply(data);
28739             }
28740         }else{
28741             html.push(this.emptyText);
28742         }
28743         this.el.update(html.join(""));
28744         this.nodes = this.el.dom.childNodes;
28745         this.updateIndexes(0);
28746     },
28747
28748     /**
28749      * 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.
28750      * @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:
28751      <pre><code>
28752      view.load({
28753          url: "your-url.php",
28754          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28755          callback: yourFunction,
28756          scope: yourObject, //(optional scope)
28757          discardUrl: false,
28758          nocache: false,
28759          text: "Loading...",
28760          timeout: 30,
28761          scripts: false
28762      });
28763      </code></pre>
28764      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28765      * 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.
28766      * @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}
28767      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28768      * @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.
28769      */
28770     load : function(){
28771         var um = this.el.getUpdateManager();
28772         um.update.apply(um, arguments);
28773     },
28774
28775     // note - render is a standard framework call...
28776     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28777     render : function(el, response){
28778         
28779         this.clearSelections();
28780         this.el.update("");
28781         var o;
28782         try{
28783             if (response != '') {
28784                 o = Roo.util.JSON.decode(response.responseText);
28785                 if(this.jsonRoot){
28786                     
28787                     o = o[this.jsonRoot];
28788                 }
28789             }
28790         } catch(e){
28791         }
28792         /**
28793          * The current JSON data or null
28794          */
28795         this.jsonData = o;
28796         this.beforeRender();
28797         this.refresh();
28798     },
28799
28800 /**
28801  * Get the number of records in the current JSON dataset
28802  * @return {Number}
28803  */
28804     getCount : function(){
28805         return this.jsonData ? this.jsonData.length : 0;
28806     },
28807
28808 /**
28809  * Returns the JSON object for the specified node(s)
28810  * @param {HTMLElement/Array} node The node or an array of nodes
28811  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28812  * you get the JSON object for the node
28813  */
28814     getNodeData : function(node){
28815         if(node instanceof Array){
28816             var data = [];
28817             for(var i = 0, len = node.length; i < len; i++){
28818                 data.push(this.getNodeData(node[i]));
28819             }
28820             return data;
28821         }
28822         return this.jsonData[this.indexOf(node)] || null;
28823     },
28824
28825     beforeRender : function(){
28826         this.snapshot = this.jsonData;
28827         if(this.sortInfo){
28828             this.sort.apply(this, this.sortInfo);
28829         }
28830         this.fireEvent("beforerender", this, this.jsonData);
28831     },
28832
28833     onLoad : function(el, o){
28834         this.fireEvent("load", this, this.jsonData, o);
28835     },
28836
28837     onLoadException : function(el, o){
28838         this.fireEvent("loadexception", this, o);
28839     },
28840
28841 /**
28842  * Filter the data by a specific property.
28843  * @param {String} property A property on your JSON objects
28844  * @param {String/RegExp} value Either string that the property values
28845  * should start with, or a RegExp to test against the property
28846  */
28847     filter : function(property, value){
28848         if(this.jsonData){
28849             var data = [];
28850             var ss = this.snapshot;
28851             if(typeof value == "string"){
28852                 var vlen = value.length;
28853                 if(vlen == 0){
28854                     this.clearFilter();
28855                     return;
28856                 }
28857                 value = value.toLowerCase();
28858                 for(var i = 0, len = ss.length; i < len; i++){
28859                     var o = ss[i];
28860                     if(o[property].substr(0, vlen).toLowerCase() == value){
28861                         data.push(o);
28862                     }
28863                 }
28864             } else if(value.exec){ // regex?
28865                 for(var i = 0, len = ss.length; i < len; i++){
28866                     var o = ss[i];
28867                     if(value.test(o[property])){
28868                         data.push(o);
28869                     }
28870                 }
28871             } else{
28872                 return;
28873             }
28874             this.jsonData = data;
28875             this.refresh();
28876         }
28877     },
28878
28879 /**
28880  * Filter by a function. The passed function will be called with each
28881  * object in the current dataset. If the function returns true the value is kept,
28882  * otherwise it is filtered.
28883  * @param {Function} fn
28884  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28885  */
28886     filterBy : function(fn, scope){
28887         if(this.jsonData){
28888             var data = [];
28889             var ss = this.snapshot;
28890             for(var i = 0, len = ss.length; i < len; i++){
28891                 var o = ss[i];
28892                 if(fn.call(scope || this, o)){
28893                     data.push(o);
28894                 }
28895             }
28896             this.jsonData = data;
28897             this.refresh();
28898         }
28899     },
28900
28901 /**
28902  * Clears the current filter.
28903  */
28904     clearFilter : function(){
28905         if(this.snapshot && this.jsonData != this.snapshot){
28906             this.jsonData = this.snapshot;
28907             this.refresh();
28908         }
28909     },
28910
28911
28912 /**
28913  * Sorts the data for this view and refreshes it.
28914  * @param {String} property A property on your JSON objects to sort on
28915  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28916  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28917  */
28918     sort : function(property, dir, sortType){
28919         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28920         if(this.jsonData){
28921             var p = property;
28922             var dsc = dir && dir.toLowerCase() == "desc";
28923             var f = function(o1, o2){
28924                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28925                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28926                 ;
28927                 if(v1 < v2){
28928                     return dsc ? +1 : -1;
28929                 } else if(v1 > v2){
28930                     return dsc ? -1 : +1;
28931                 } else{
28932                     return 0;
28933                 }
28934             };
28935             this.jsonData.sort(f);
28936             this.refresh();
28937             if(this.jsonData != this.snapshot){
28938                 this.snapshot.sort(f);
28939             }
28940         }
28941     }
28942 });/*
28943  * Based on:
28944  * Ext JS Library 1.1.1
28945  * Copyright(c) 2006-2007, Ext JS, LLC.
28946  *
28947  * Originally Released Under LGPL - original licence link has changed is not relivant.
28948  *
28949  * Fork - LGPL
28950  * <script type="text/javascript">
28951  */
28952  
28953
28954 /**
28955  * @class Roo.ColorPalette
28956  * @extends Roo.Component
28957  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28958  * Here's an example of typical usage:
28959  * <pre><code>
28960 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28961 cp.render('my-div');
28962
28963 cp.on('select', function(palette, selColor){
28964     // do something with selColor
28965 });
28966 </code></pre>
28967  * @constructor
28968  * Create a new ColorPalette
28969  * @param {Object} config The config object
28970  */
28971 Roo.ColorPalette = function(config){
28972     Roo.ColorPalette.superclass.constructor.call(this, config);
28973     this.addEvents({
28974         /**
28975              * @event select
28976              * Fires when a color is selected
28977              * @param {ColorPalette} this
28978              * @param {String} color The 6-digit color hex code (without the # symbol)
28979              */
28980         select: true
28981     });
28982
28983     if(this.handler){
28984         this.on("select", this.handler, this.scope, true);
28985     }
28986 };
28987 Roo.extend(Roo.ColorPalette, Roo.Component, {
28988     /**
28989      * @cfg {String} itemCls
28990      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28991      */
28992     itemCls : "x-color-palette",
28993     /**
28994      * @cfg {String} value
28995      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28996      * the hex codes are case-sensitive.
28997      */
28998     value : null,
28999     clickEvent:'click',
29000     // private
29001     ctype: "Roo.ColorPalette",
29002
29003     /**
29004      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
29005      */
29006     allowReselect : false,
29007
29008     /**
29009      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
29010      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
29011      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
29012      * of colors with the width setting until the box is symmetrical.</p>
29013      * <p>You can override individual colors if needed:</p>
29014      * <pre><code>
29015 var cp = new Roo.ColorPalette();
29016 cp.colors[0] = "FF0000";  // change the first box to red
29017 </code></pre>
29018
29019 Or you can provide a custom array of your own for complete control:
29020 <pre><code>
29021 var cp = new Roo.ColorPalette();
29022 cp.colors = ["000000", "993300", "333300"];
29023 </code></pre>
29024      * @type Array
29025      */
29026     colors : [
29027         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
29028         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
29029         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
29030         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
29031         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
29032     ],
29033
29034     // private
29035     onRender : function(container, position){
29036         var t = new Roo.MasterTemplate(
29037             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
29038         );
29039         var c = this.colors;
29040         for(var i = 0, len = c.length; i < len; i++){
29041             t.add([c[i]]);
29042         }
29043         var el = document.createElement("div");
29044         el.className = this.itemCls;
29045         t.overwrite(el);
29046         container.dom.insertBefore(el, position);
29047         this.el = Roo.get(el);
29048         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
29049         if(this.clickEvent != 'click'){
29050             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
29051         }
29052     },
29053
29054     // private
29055     afterRender : function(){
29056         Roo.ColorPalette.superclass.afterRender.call(this);
29057         if(this.value){
29058             var s = this.value;
29059             this.value = null;
29060             this.select(s);
29061         }
29062     },
29063
29064     // private
29065     handleClick : function(e, t){
29066         e.preventDefault();
29067         if(!this.disabled){
29068             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
29069             this.select(c.toUpperCase());
29070         }
29071     },
29072
29073     /**
29074      * Selects the specified color in the palette (fires the select event)
29075      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
29076      */
29077     select : function(color){
29078         color = color.replace("#", "");
29079         if(color != this.value || this.allowReselect){
29080             var el = this.el;
29081             if(this.value){
29082                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
29083             }
29084             el.child("a.color-"+color).addClass("x-color-palette-sel");
29085             this.value = color;
29086             this.fireEvent("select", this, color);
29087         }
29088     }
29089 });/*
29090  * Based on:
29091  * Ext JS Library 1.1.1
29092  * Copyright(c) 2006-2007, Ext JS, LLC.
29093  *
29094  * Originally Released Under LGPL - original licence link has changed is not relivant.
29095  *
29096  * Fork - LGPL
29097  * <script type="text/javascript">
29098  */
29099  
29100 /**
29101  * @class Roo.DatePicker
29102  * @extends Roo.Component
29103  * Simple date picker class.
29104  * @constructor
29105  * Create a new DatePicker
29106  * @param {Object} config The config object
29107  */
29108 Roo.DatePicker = function(config){
29109     Roo.DatePicker.superclass.constructor.call(this, config);
29110
29111     this.value = config && config.value ?
29112                  config.value.clearTime() : new Date().clearTime();
29113
29114     this.addEvents({
29115         /**
29116              * @event select
29117              * Fires when a date is selected
29118              * @param {DatePicker} this
29119              * @param {Date} date The selected date
29120              */
29121         'select': true,
29122         /**
29123              * @event monthchange
29124              * Fires when the displayed month changes 
29125              * @param {DatePicker} this
29126              * @param {Date} date The selected month
29127              */
29128         'monthchange': true
29129     });
29130
29131     if(this.handler){
29132         this.on("select", this.handler,  this.scope || this);
29133     }
29134     // build the disabledDatesRE
29135     if(!this.disabledDatesRE && this.disabledDates){
29136         var dd = this.disabledDates;
29137         var re = "(?:";
29138         for(var i = 0; i < dd.length; i++){
29139             re += dd[i];
29140             if(i != dd.length-1) {
29141                 re += "|";
29142             }
29143         }
29144         this.disabledDatesRE = new RegExp(re + ")");
29145     }
29146 };
29147
29148 Roo.extend(Roo.DatePicker, Roo.Component, {
29149     /**
29150      * @cfg {String} todayText
29151      * The text to display on the button that selects the current date (defaults to "Today")
29152      */
29153     todayText : "Today",
29154     /**
29155      * @cfg {String} okText
29156      * The text to display on the ok button
29157      */
29158     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
29159     /**
29160      * @cfg {String} cancelText
29161      * The text to display on the cancel button
29162      */
29163     cancelText : "Cancel",
29164     /**
29165      * @cfg {String} todayTip
29166      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
29167      */
29168     todayTip : "{0} (Spacebar)",
29169     /**
29170      * @cfg {Date} minDate
29171      * Minimum allowable date (JavaScript date object, defaults to null)
29172      */
29173     minDate : null,
29174     /**
29175      * @cfg {Date} maxDate
29176      * Maximum allowable date (JavaScript date object, defaults to null)
29177      */
29178     maxDate : null,
29179     /**
29180      * @cfg {String} minText
29181      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
29182      */
29183     minText : "This date is before the minimum date",
29184     /**
29185      * @cfg {String} maxText
29186      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
29187      */
29188     maxText : "This date is after the maximum date",
29189     /**
29190      * @cfg {String} format
29191      * The default date format string which can be overriden for localization support.  The format must be
29192      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
29193      */
29194     format : "m/d/y",
29195     /**
29196      * @cfg {Array} disabledDays
29197      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
29198      */
29199     disabledDays : null,
29200     /**
29201      * @cfg {String} disabledDaysText
29202      * The tooltip to display when the date falls on a disabled day (defaults to "")
29203      */
29204     disabledDaysText : "",
29205     /**
29206      * @cfg {RegExp} disabledDatesRE
29207      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
29208      */
29209     disabledDatesRE : null,
29210     /**
29211      * @cfg {String} disabledDatesText
29212      * The tooltip text to display when the date falls on a disabled date (defaults to "")
29213      */
29214     disabledDatesText : "",
29215     /**
29216      * @cfg {Boolean} constrainToViewport
29217      * True to constrain the date picker to the viewport (defaults to true)
29218      */
29219     constrainToViewport : true,
29220     /**
29221      * @cfg {Array} monthNames
29222      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
29223      */
29224     monthNames : Date.monthNames,
29225     /**
29226      * @cfg {Array} dayNames
29227      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
29228      */
29229     dayNames : Date.dayNames,
29230     /**
29231      * @cfg {String} nextText
29232      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
29233      */
29234     nextText: 'Next Month (Control+Right)',
29235     /**
29236      * @cfg {String} prevText
29237      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
29238      */
29239     prevText: 'Previous Month (Control+Left)',
29240     /**
29241      * @cfg {String} monthYearText
29242      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
29243      */
29244     monthYearText: 'Choose a month (Control+Up/Down to move years)',
29245     /**
29246      * @cfg {Number} startDay
29247      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
29248      */
29249     startDay : 0,
29250     /**
29251      * @cfg {Bool} showClear
29252      * Show a clear button (usefull for date form elements that can be blank.)
29253      */
29254     
29255     showClear: false,
29256     
29257     /**
29258      * Sets the value of the date field
29259      * @param {Date} value The date to set
29260      */
29261     setValue : function(value){
29262         var old = this.value;
29263         
29264         if (typeof(value) == 'string') {
29265          
29266             value = Date.parseDate(value, this.format);
29267         }
29268         if (!value) {
29269             value = new Date();
29270         }
29271         
29272         this.value = value.clearTime(true);
29273         if(this.el){
29274             this.update(this.value);
29275         }
29276     },
29277
29278     /**
29279      * Gets the current selected value of the date field
29280      * @return {Date} The selected date
29281      */
29282     getValue : function(){
29283         return this.value;
29284     },
29285
29286     // private
29287     focus : function(){
29288         if(this.el){
29289             this.update(this.activeDate);
29290         }
29291     },
29292
29293     // privateval
29294     onRender : function(container, position){
29295         
29296         var m = [
29297              '<table cellspacing="0">',
29298                 '<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>',
29299                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
29300         var dn = this.dayNames;
29301         for(var i = 0; i < 7; i++){
29302             var d = this.startDay+i;
29303             if(d > 6){
29304                 d = d-7;
29305             }
29306             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
29307         }
29308         m[m.length] = "</tr></thead><tbody><tr>";
29309         for(var i = 0; i < 42; i++) {
29310             if(i % 7 == 0 && i != 0){
29311                 m[m.length] = "</tr><tr>";
29312             }
29313             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
29314         }
29315         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
29316             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
29317
29318         var el = document.createElement("div");
29319         el.className = "x-date-picker";
29320         el.innerHTML = m.join("");
29321
29322         container.dom.insertBefore(el, position);
29323
29324         this.el = Roo.get(el);
29325         this.eventEl = Roo.get(el.firstChild);
29326
29327         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
29328             handler: this.showPrevMonth,
29329             scope: this,
29330             preventDefault:true,
29331             stopDefault:true
29332         });
29333
29334         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29335             handler: this.showNextMonth,
29336             scope: this,
29337             preventDefault:true,
29338             stopDefault:true
29339         });
29340
29341         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29342
29343         this.monthPicker = this.el.down('div.x-date-mp');
29344         this.monthPicker.enableDisplayMode('block');
29345         
29346         var kn = new Roo.KeyNav(this.eventEl, {
29347             "left" : function(e){
29348                 e.ctrlKey ?
29349                     this.showPrevMonth() :
29350                     this.update(this.activeDate.add("d", -1));
29351             },
29352
29353             "right" : function(e){
29354                 e.ctrlKey ?
29355                     this.showNextMonth() :
29356                     this.update(this.activeDate.add("d", 1));
29357             },
29358
29359             "up" : function(e){
29360                 e.ctrlKey ?
29361                     this.showNextYear() :
29362                     this.update(this.activeDate.add("d", -7));
29363             },
29364
29365             "down" : function(e){
29366                 e.ctrlKey ?
29367                     this.showPrevYear() :
29368                     this.update(this.activeDate.add("d", 7));
29369             },
29370
29371             "pageUp" : function(e){
29372                 this.showNextMonth();
29373             },
29374
29375             "pageDown" : function(e){
29376                 this.showPrevMonth();
29377             },
29378
29379             "enter" : function(e){
29380                 e.stopPropagation();
29381                 return true;
29382             },
29383
29384             scope : this
29385         });
29386
29387         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29388
29389         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29390
29391         this.el.unselectable();
29392         
29393         this.cells = this.el.select("table.x-date-inner tbody td");
29394         this.textNodes = this.el.query("table.x-date-inner tbody span");
29395
29396         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29397             text: "&#160;",
29398             tooltip: this.monthYearText
29399         });
29400
29401         this.mbtn.on('click', this.showMonthPicker, this);
29402         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29403
29404
29405         var today = (new Date()).dateFormat(this.format);
29406         
29407         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29408         if (this.showClear) {
29409             baseTb.add( new Roo.Toolbar.Fill());
29410         }
29411         baseTb.add({
29412             text: String.format(this.todayText, today),
29413             tooltip: String.format(this.todayTip, today),
29414             handler: this.selectToday,
29415             scope: this
29416         });
29417         
29418         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29419             
29420         //});
29421         if (this.showClear) {
29422             
29423             baseTb.add( new Roo.Toolbar.Fill());
29424             baseTb.add({
29425                 text: '&#160;',
29426                 cls: 'x-btn-icon x-btn-clear',
29427                 handler: function() {
29428                     //this.value = '';
29429                     this.fireEvent("select", this, '');
29430                 },
29431                 scope: this
29432             });
29433         }
29434         
29435         
29436         if(Roo.isIE){
29437             this.el.repaint();
29438         }
29439         this.update(this.value);
29440     },
29441
29442     createMonthPicker : function(){
29443         if(!this.monthPicker.dom.firstChild){
29444             var buf = ['<table border="0" cellspacing="0">'];
29445             for(var i = 0; i < 6; i++){
29446                 buf.push(
29447                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29448                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29449                     i == 0 ?
29450                     '<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>' :
29451                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29452                 );
29453             }
29454             buf.push(
29455                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29456                     this.okText,
29457                     '</button><button type="button" class="x-date-mp-cancel">',
29458                     this.cancelText,
29459                     '</button></td></tr>',
29460                 '</table>'
29461             );
29462             this.monthPicker.update(buf.join(''));
29463             this.monthPicker.on('click', this.onMonthClick, this);
29464             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29465
29466             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29467             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29468
29469             this.mpMonths.each(function(m, a, i){
29470                 i += 1;
29471                 if((i%2) == 0){
29472                     m.dom.xmonth = 5 + Math.round(i * .5);
29473                 }else{
29474                     m.dom.xmonth = Math.round((i-1) * .5);
29475                 }
29476             });
29477         }
29478     },
29479
29480     showMonthPicker : function(){
29481         this.createMonthPicker();
29482         var size = this.el.getSize();
29483         this.monthPicker.setSize(size);
29484         this.monthPicker.child('table').setSize(size);
29485
29486         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29487         this.updateMPMonth(this.mpSelMonth);
29488         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29489         this.updateMPYear(this.mpSelYear);
29490
29491         this.monthPicker.slideIn('t', {duration:.2});
29492     },
29493
29494     updateMPYear : function(y){
29495         this.mpyear = y;
29496         var ys = this.mpYears.elements;
29497         for(var i = 1; i <= 10; i++){
29498             var td = ys[i-1], y2;
29499             if((i%2) == 0){
29500                 y2 = y + Math.round(i * .5);
29501                 td.firstChild.innerHTML = y2;
29502                 td.xyear = y2;
29503             }else{
29504                 y2 = y - (5-Math.round(i * .5));
29505                 td.firstChild.innerHTML = y2;
29506                 td.xyear = y2;
29507             }
29508             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29509         }
29510     },
29511
29512     updateMPMonth : function(sm){
29513         this.mpMonths.each(function(m, a, i){
29514             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29515         });
29516     },
29517
29518     selectMPMonth: function(m){
29519         
29520     },
29521
29522     onMonthClick : function(e, t){
29523         e.stopEvent();
29524         var el = new Roo.Element(t), pn;
29525         if(el.is('button.x-date-mp-cancel')){
29526             this.hideMonthPicker();
29527         }
29528         else if(el.is('button.x-date-mp-ok')){
29529             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29530             this.hideMonthPicker();
29531         }
29532         else if(pn = el.up('td.x-date-mp-month', 2)){
29533             this.mpMonths.removeClass('x-date-mp-sel');
29534             pn.addClass('x-date-mp-sel');
29535             this.mpSelMonth = pn.dom.xmonth;
29536         }
29537         else if(pn = el.up('td.x-date-mp-year', 2)){
29538             this.mpYears.removeClass('x-date-mp-sel');
29539             pn.addClass('x-date-mp-sel');
29540             this.mpSelYear = pn.dom.xyear;
29541         }
29542         else if(el.is('a.x-date-mp-prev')){
29543             this.updateMPYear(this.mpyear-10);
29544         }
29545         else if(el.is('a.x-date-mp-next')){
29546             this.updateMPYear(this.mpyear+10);
29547         }
29548     },
29549
29550     onMonthDblClick : function(e, t){
29551         e.stopEvent();
29552         var el = new Roo.Element(t), pn;
29553         if(pn = el.up('td.x-date-mp-month', 2)){
29554             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29555             this.hideMonthPicker();
29556         }
29557         else if(pn = el.up('td.x-date-mp-year', 2)){
29558             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29559             this.hideMonthPicker();
29560         }
29561     },
29562
29563     hideMonthPicker : function(disableAnim){
29564         if(this.monthPicker){
29565             if(disableAnim === true){
29566                 this.monthPicker.hide();
29567             }else{
29568                 this.monthPicker.slideOut('t', {duration:.2});
29569             }
29570         }
29571     },
29572
29573     // private
29574     showPrevMonth : function(e){
29575         this.update(this.activeDate.add("mo", -1));
29576     },
29577
29578     // private
29579     showNextMonth : function(e){
29580         this.update(this.activeDate.add("mo", 1));
29581     },
29582
29583     // private
29584     showPrevYear : function(){
29585         this.update(this.activeDate.add("y", -1));
29586     },
29587
29588     // private
29589     showNextYear : function(){
29590         this.update(this.activeDate.add("y", 1));
29591     },
29592
29593     // private
29594     handleMouseWheel : function(e){
29595         var delta = e.getWheelDelta();
29596         if(delta > 0){
29597             this.showPrevMonth();
29598             e.stopEvent();
29599         } else if(delta < 0){
29600             this.showNextMonth();
29601             e.stopEvent();
29602         }
29603     },
29604
29605     // private
29606     handleDateClick : function(e, t){
29607         e.stopEvent();
29608         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29609             this.setValue(new Date(t.dateValue));
29610             this.fireEvent("select", this, this.value);
29611         }
29612     },
29613
29614     // private
29615     selectToday : function(){
29616         this.setValue(new Date().clearTime());
29617         this.fireEvent("select", this, this.value);
29618     },
29619
29620     // private
29621     update : function(date)
29622     {
29623         var vd = this.activeDate;
29624         this.activeDate = date;
29625         if(vd && this.el){
29626             var t = date.getTime();
29627             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29628                 this.cells.removeClass("x-date-selected");
29629                 this.cells.each(function(c){
29630                    if(c.dom.firstChild.dateValue == t){
29631                        c.addClass("x-date-selected");
29632                        setTimeout(function(){
29633                             try{c.dom.firstChild.focus();}catch(e){}
29634                        }, 50);
29635                        return false;
29636                    }
29637                 });
29638                 return;
29639             }
29640         }
29641         
29642         var days = date.getDaysInMonth();
29643         var firstOfMonth = date.getFirstDateOfMonth();
29644         var startingPos = firstOfMonth.getDay()-this.startDay;
29645
29646         if(startingPos <= this.startDay){
29647             startingPos += 7;
29648         }
29649
29650         var pm = date.add("mo", -1);
29651         var prevStart = pm.getDaysInMonth()-startingPos;
29652
29653         var cells = this.cells.elements;
29654         var textEls = this.textNodes;
29655         days += startingPos;
29656
29657         // convert everything to numbers so it's fast
29658         var day = 86400000;
29659         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29660         var today = new Date().clearTime().getTime();
29661         var sel = date.clearTime().getTime();
29662         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29663         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29664         var ddMatch = this.disabledDatesRE;
29665         var ddText = this.disabledDatesText;
29666         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29667         var ddaysText = this.disabledDaysText;
29668         var format = this.format;
29669
29670         var setCellClass = function(cal, cell){
29671             cell.title = "";
29672             var t = d.getTime();
29673             cell.firstChild.dateValue = t;
29674             if(t == today){
29675                 cell.className += " x-date-today";
29676                 cell.title = cal.todayText;
29677             }
29678             if(t == sel){
29679                 cell.className += " x-date-selected";
29680                 setTimeout(function(){
29681                     try{cell.firstChild.focus();}catch(e){}
29682                 }, 50);
29683             }
29684             // disabling
29685             if(t < min) {
29686                 cell.className = " x-date-disabled";
29687                 cell.title = cal.minText;
29688                 return;
29689             }
29690             if(t > max) {
29691                 cell.className = " x-date-disabled";
29692                 cell.title = cal.maxText;
29693                 return;
29694             }
29695             if(ddays){
29696                 if(ddays.indexOf(d.getDay()) != -1){
29697                     cell.title = ddaysText;
29698                     cell.className = " x-date-disabled";
29699                 }
29700             }
29701             if(ddMatch && format){
29702                 var fvalue = d.dateFormat(format);
29703                 if(ddMatch.test(fvalue)){
29704                     cell.title = ddText.replace("%0", fvalue);
29705                     cell.className = " x-date-disabled";
29706                 }
29707             }
29708         };
29709
29710         var i = 0;
29711         for(; i < startingPos; i++) {
29712             textEls[i].innerHTML = (++prevStart);
29713             d.setDate(d.getDate()+1);
29714             cells[i].className = "x-date-prevday";
29715             setCellClass(this, cells[i]);
29716         }
29717         for(; i < days; i++){
29718             intDay = i - startingPos + 1;
29719             textEls[i].innerHTML = (intDay);
29720             d.setDate(d.getDate()+1);
29721             cells[i].className = "x-date-active";
29722             setCellClass(this, cells[i]);
29723         }
29724         var extraDays = 0;
29725         for(; i < 42; i++) {
29726              textEls[i].innerHTML = (++extraDays);
29727              d.setDate(d.getDate()+1);
29728              cells[i].className = "x-date-nextday";
29729              setCellClass(this, cells[i]);
29730         }
29731
29732         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29733         this.fireEvent('monthchange', this, date);
29734         
29735         if(!this.internalRender){
29736             var main = this.el.dom.firstChild;
29737             var w = main.offsetWidth;
29738             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29739             Roo.fly(main).setWidth(w);
29740             this.internalRender = true;
29741             // opera does not respect the auto grow header center column
29742             // then, after it gets a width opera refuses to recalculate
29743             // without a second pass
29744             if(Roo.isOpera && !this.secondPass){
29745                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29746                 this.secondPass = true;
29747                 this.update.defer(10, this, [date]);
29748             }
29749         }
29750         
29751         
29752     }
29753 });        /*
29754  * Based on:
29755  * Ext JS Library 1.1.1
29756  * Copyright(c) 2006-2007, Ext JS, LLC.
29757  *
29758  * Originally Released Under LGPL - original licence link has changed is not relivant.
29759  *
29760  * Fork - LGPL
29761  * <script type="text/javascript">
29762  */
29763 /**
29764  * @class Roo.TabPanel
29765  * @extends Roo.util.Observable
29766  * A lightweight tab container.
29767  * <br><br>
29768  * Usage:
29769  * <pre><code>
29770 // basic tabs 1, built from existing content
29771 var tabs = new Roo.TabPanel("tabs1");
29772 tabs.addTab("script", "View Script");
29773 tabs.addTab("markup", "View Markup");
29774 tabs.activate("script");
29775
29776 // more advanced tabs, built from javascript
29777 var jtabs = new Roo.TabPanel("jtabs");
29778 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29779
29780 // set up the UpdateManager
29781 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29782 var updater = tab2.getUpdateManager();
29783 updater.setDefaultUrl("ajax1.htm");
29784 tab2.on('activate', updater.refresh, updater, true);
29785
29786 // Use setUrl for Ajax loading
29787 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29788 tab3.setUrl("ajax2.htm", null, true);
29789
29790 // Disabled tab
29791 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29792 tab4.disable();
29793
29794 jtabs.activate("jtabs-1");
29795  * </code></pre>
29796  * @constructor
29797  * Create a new TabPanel.
29798  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29799  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29800  */
29801 Roo.TabPanel = function(container, config){
29802     /**
29803     * The container element for this TabPanel.
29804     * @type Roo.Element
29805     */
29806     this.el = Roo.get(container, true);
29807     if(config){
29808         if(typeof config == "boolean"){
29809             this.tabPosition = config ? "bottom" : "top";
29810         }else{
29811             Roo.apply(this, config);
29812         }
29813     }
29814     if(this.tabPosition == "bottom"){
29815         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29816         this.el.addClass("x-tabs-bottom");
29817     }
29818     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29819     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29820     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29821     if(Roo.isIE){
29822         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29823     }
29824     if(this.tabPosition != "bottom"){
29825         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29826          * @type Roo.Element
29827          */
29828         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29829         this.el.addClass("x-tabs-top");
29830     }
29831     this.items = [];
29832
29833     this.bodyEl.setStyle("position", "relative");
29834
29835     this.active = null;
29836     this.activateDelegate = this.activate.createDelegate(this);
29837
29838     this.addEvents({
29839         /**
29840          * @event tabchange
29841          * Fires when the active tab changes
29842          * @param {Roo.TabPanel} this
29843          * @param {Roo.TabPanelItem} activePanel The new active tab
29844          */
29845         "tabchange": true,
29846         /**
29847          * @event beforetabchange
29848          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29849          * @param {Roo.TabPanel} this
29850          * @param {Object} e Set cancel to true on this object to cancel the tab change
29851          * @param {Roo.TabPanelItem} tab The tab being changed to
29852          */
29853         "beforetabchange" : true
29854     });
29855
29856     Roo.EventManager.onWindowResize(this.onResize, this);
29857     this.cpad = this.el.getPadding("lr");
29858     this.hiddenCount = 0;
29859
29860
29861     // toolbar on the tabbar support...
29862     if (this.toolbar) {
29863         var tcfg = this.toolbar;
29864         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29865         this.toolbar = new Roo.Toolbar(tcfg);
29866         if (Roo.isSafari) {
29867             var tbl = tcfg.container.child('table', true);
29868             tbl.setAttribute('width', '100%');
29869         }
29870         
29871     }
29872    
29873
29874
29875     Roo.TabPanel.superclass.constructor.call(this);
29876 };
29877
29878 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29879     /*
29880      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29881      */
29882     tabPosition : "top",
29883     /*
29884      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29885      */
29886     currentTabWidth : 0,
29887     /*
29888      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29889      */
29890     minTabWidth : 40,
29891     /*
29892      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29893      */
29894     maxTabWidth : 250,
29895     /*
29896      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29897      */
29898     preferredTabWidth : 175,
29899     /*
29900      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29901      */
29902     resizeTabs : false,
29903     /*
29904      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29905      */
29906     monitorResize : true,
29907     /*
29908      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29909      */
29910     toolbar : false,
29911
29912     /**
29913      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29914      * @param {String} id The id of the div to use <b>or create</b>
29915      * @param {String} text The text for the tab
29916      * @param {String} content (optional) Content to put in the TabPanelItem body
29917      * @param {Boolean} closable (optional) True to create a close icon on the tab
29918      * @return {Roo.TabPanelItem} The created TabPanelItem
29919      */
29920     addTab : function(id, text, content, closable){
29921         var item = new Roo.TabPanelItem(this, id, text, closable);
29922         this.addTabItem(item);
29923         if(content){
29924             item.setContent(content);
29925         }
29926         return item;
29927     },
29928
29929     /**
29930      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29931      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29932      * @return {Roo.TabPanelItem}
29933      */
29934     getTab : function(id){
29935         return this.items[id];
29936     },
29937
29938     /**
29939      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29940      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29941      */
29942     hideTab : function(id){
29943         var t = this.items[id];
29944         if(!t.isHidden()){
29945            t.setHidden(true);
29946            this.hiddenCount++;
29947            this.autoSizeTabs();
29948         }
29949     },
29950
29951     /**
29952      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29953      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29954      */
29955     unhideTab : function(id){
29956         var t = this.items[id];
29957         if(t.isHidden()){
29958            t.setHidden(false);
29959            this.hiddenCount--;
29960            this.autoSizeTabs();
29961         }
29962     },
29963
29964     /**
29965      * Adds an existing {@link Roo.TabPanelItem}.
29966      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29967      */
29968     addTabItem : function(item){
29969         this.items[item.id] = item;
29970         this.items.push(item);
29971         if(this.resizeTabs){
29972            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29973            this.autoSizeTabs();
29974         }else{
29975             item.autoSize();
29976         }
29977     },
29978
29979     /**
29980      * Removes a {@link Roo.TabPanelItem}.
29981      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29982      */
29983     removeTab : function(id){
29984         var items = this.items;
29985         var tab = items[id];
29986         if(!tab) { return; }
29987         var index = items.indexOf(tab);
29988         if(this.active == tab && items.length > 1){
29989             var newTab = this.getNextAvailable(index);
29990             if(newTab) {
29991                 newTab.activate();
29992             }
29993         }
29994         this.stripEl.dom.removeChild(tab.pnode.dom);
29995         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29996             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29997         }
29998         items.splice(index, 1);
29999         delete this.items[tab.id];
30000         tab.fireEvent("close", tab);
30001         tab.purgeListeners();
30002         this.autoSizeTabs();
30003     },
30004
30005     getNextAvailable : function(start){
30006         var items = this.items;
30007         var index = start;
30008         // look for a next tab that will slide over to
30009         // replace the one being removed
30010         while(index < items.length){
30011             var item = items[++index];
30012             if(item && !item.isHidden()){
30013                 return item;
30014             }
30015         }
30016         // if one isn't found select the previous tab (on the left)
30017         index = start;
30018         while(index >= 0){
30019             var item = items[--index];
30020             if(item && !item.isHidden()){
30021                 return item;
30022             }
30023         }
30024         return null;
30025     },
30026
30027     /**
30028      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
30029      * @param {String/Number} id The id or index of the TabPanelItem to disable.
30030      */
30031     disableTab : function(id){
30032         var tab = this.items[id];
30033         if(tab && this.active != tab){
30034             tab.disable();
30035         }
30036     },
30037
30038     /**
30039      * Enables a {@link Roo.TabPanelItem} that is disabled.
30040      * @param {String/Number} id The id or index of the TabPanelItem to enable.
30041      */
30042     enableTab : function(id){
30043         var tab = this.items[id];
30044         tab.enable();
30045     },
30046
30047     /**
30048      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
30049      * @param {String/Number} id The id or index of the TabPanelItem to activate.
30050      * @return {Roo.TabPanelItem} The TabPanelItem.
30051      */
30052     activate : function(id){
30053         var tab = this.items[id];
30054         if(!tab){
30055             return null;
30056         }
30057         if(tab == this.active || tab.disabled){
30058             return tab;
30059         }
30060         var e = {};
30061         this.fireEvent("beforetabchange", this, e, tab);
30062         if(e.cancel !== true && !tab.disabled){
30063             if(this.active){
30064                 this.active.hide();
30065             }
30066             this.active = this.items[id];
30067             this.active.show();
30068             this.fireEvent("tabchange", this, this.active);
30069         }
30070         return tab;
30071     },
30072
30073     /**
30074      * Gets the active {@link Roo.TabPanelItem}.
30075      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
30076      */
30077     getActiveTab : function(){
30078         return this.active;
30079     },
30080
30081     /**
30082      * Updates the tab body element to fit the height of the container element
30083      * for overflow scrolling
30084      * @param {Number} targetHeight (optional) Override the starting height from the elements height
30085      */
30086     syncHeight : function(targetHeight){
30087         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
30088         var bm = this.bodyEl.getMargins();
30089         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
30090         this.bodyEl.setHeight(newHeight);
30091         return newHeight;
30092     },
30093
30094     onResize : function(){
30095         if(this.monitorResize){
30096             this.autoSizeTabs();
30097         }
30098     },
30099
30100     /**
30101      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
30102      */
30103     beginUpdate : function(){
30104         this.updating = true;
30105     },
30106
30107     /**
30108      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
30109      */
30110     endUpdate : function(){
30111         this.updating = false;
30112         this.autoSizeTabs();
30113     },
30114
30115     /**
30116      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
30117      */
30118     autoSizeTabs : function(){
30119         var count = this.items.length;
30120         var vcount = count - this.hiddenCount;
30121         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
30122             return;
30123         }
30124         var w = Math.max(this.el.getWidth() - this.cpad, 10);
30125         var availWidth = Math.floor(w / vcount);
30126         var b = this.stripBody;
30127         if(b.getWidth() > w){
30128             var tabs = this.items;
30129             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
30130             if(availWidth < this.minTabWidth){
30131                 /*if(!this.sleft){    // incomplete scrolling code
30132                     this.createScrollButtons();
30133                 }
30134                 this.showScroll();
30135                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
30136             }
30137         }else{
30138             if(this.currentTabWidth < this.preferredTabWidth){
30139                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
30140             }
30141         }
30142     },
30143
30144     /**
30145      * Returns the number of tabs in this TabPanel.
30146      * @return {Number}
30147      */
30148      getCount : function(){
30149          return this.items.length;
30150      },
30151
30152     /**
30153      * Resizes all the tabs to the passed width
30154      * @param {Number} The new width
30155      */
30156     setTabWidth : function(width){
30157         this.currentTabWidth = width;
30158         for(var i = 0, len = this.items.length; i < len; i++) {
30159                 if(!this.items[i].isHidden()) {
30160                 this.items[i].setWidth(width);
30161             }
30162         }
30163     },
30164
30165     /**
30166      * Destroys this TabPanel
30167      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
30168      */
30169     destroy : function(removeEl){
30170         Roo.EventManager.removeResizeListener(this.onResize, this);
30171         for(var i = 0, len = this.items.length; i < len; i++){
30172             this.items[i].purgeListeners();
30173         }
30174         if(removeEl === true){
30175             this.el.update("");
30176             this.el.remove();
30177         }
30178     }
30179 });
30180
30181 /**
30182  * @class Roo.TabPanelItem
30183  * @extends Roo.util.Observable
30184  * Represents an individual item (tab plus body) in a TabPanel.
30185  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
30186  * @param {String} id The id of this TabPanelItem
30187  * @param {String} text The text for the tab of this TabPanelItem
30188  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
30189  */
30190 Roo.TabPanelItem = function(tabPanel, id, text, closable){
30191     /**
30192      * The {@link Roo.TabPanel} this TabPanelItem belongs to
30193      * @type Roo.TabPanel
30194      */
30195     this.tabPanel = tabPanel;
30196     /**
30197      * The id for this TabPanelItem
30198      * @type String
30199      */
30200     this.id = id;
30201     /** @private */
30202     this.disabled = false;
30203     /** @private */
30204     this.text = text;
30205     /** @private */
30206     this.loaded = false;
30207     this.closable = closable;
30208
30209     /**
30210      * The body element for this TabPanelItem.
30211      * @type Roo.Element
30212      */
30213     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
30214     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
30215     this.bodyEl.setStyle("display", "block");
30216     this.bodyEl.setStyle("zoom", "1");
30217     this.hideAction();
30218
30219     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
30220     /** @private */
30221     this.el = Roo.get(els.el, true);
30222     this.inner = Roo.get(els.inner, true);
30223     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
30224     this.pnode = Roo.get(els.el.parentNode, true);
30225     this.el.on("mousedown", this.onTabMouseDown, this);
30226     this.el.on("click", this.onTabClick, this);
30227     /** @private */
30228     if(closable){
30229         var c = Roo.get(els.close, true);
30230         c.dom.title = this.closeText;
30231         c.addClassOnOver("close-over");
30232         c.on("click", this.closeClick, this);
30233      }
30234
30235     this.addEvents({
30236          /**
30237          * @event activate
30238          * Fires when this tab becomes the active tab.
30239          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30240          * @param {Roo.TabPanelItem} this
30241          */
30242         "activate": true,
30243         /**
30244          * @event beforeclose
30245          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
30246          * @param {Roo.TabPanelItem} this
30247          * @param {Object} e Set cancel to true on this object to cancel the close.
30248          */
30249         "beforeclose": true,
30250         /**
30251          * @event close
30252          * Fires when this tab is closed.
30253          * @param {Roo.TabPanelItem} this
30254          */
30255          "close": true,
30256         /**
30257          * @event deactivate
30258          * Fires when this tab is no longer the active tab.
30259          * @param {Roo.TabPanel} tabPanel The parent TabPanel
30260          * @param {Roo.TabPanelItem} this
30261          */
30262          "deactivate" : true
30263     });
30264     this.hidden = false;
30265
30266     Roo.TabPanelItem.superclass.constructor.call(this);
30267 };
30268
30269 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
30270     purgeListeners : function(){
30271        Roo.util.Observable.prototype.purgeListeners.call(this);
30272        this.el.removeAllListeners();
30273     },
30274     /**
30275      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
30276      */
30277     show : function(){
30278         this.pnode.addClass("on");
30279         this.showAction();
30280         if(Roo.isOpera){
30281             this.tabPanel.stripWrap.repaint();
30282         }
30283         this.fireEvent("activate", this.tabPanel, this);
30284     },
30285
30286     /**
30287      * Returns true if this tab is the active tab.
30288      * @return {Boolean}
30289      */
30290     isActive : function(){
30291         return this.tabPanel.getActiveTab() == this;
30292     },
30293
30294     /**
30295      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
30296      */
30297     hide : function(){
30298         this.pnode.removeClass("on");
30299         this.hideAction();
30300         this.fireEvent("deactivate", this.tabPanel, this);
30301     },
30302
30303     hideAction : function(){
30304         this.bodyEl.hide();
30305         this.bodyEl.setStyle("position", "absolute");
30306         this.bodyEl.setLeft("-20000px");
30307         this.bodyEl.setTop("-20000px");
30308     },
30309
30310     showAction : function(){
30311         this.bodyEl.setStyle("position", "relative");
30312         this.bodyEl.setTop("");
30313         this.bodyEl.setLeft("");
30314         this.bodyEl.show();
30315     },
30316
30317     /**
30318      * Set the tooltip for the tab.
30319      * @param {String} tooltip The tab's tooltip
30320      */
30321     setTooltip : function(text){
30322         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
30323             this.textEl.dom.qtip = text;
30324             this.textEl.dom.removeAttribute('title');
30325         }else{
30326             this.textEl.dom.title = text;
30327         }
30328     },
30329
30330     onTabClick : function(e){
30331         e.preventDefault();
30332         this.tabPanel.activate(this.id);
30333     },
30334
30335     onTabMouseDown : function(e){
30336         e.preventDefault();
30337         this.tabPanel.activate(this.id);
30338     },
30339
30340     getWidth : function(){
30341         return this.inner.getWidth();
30342     },
30343
30344     setWidth : function(width){
30345         var iwidth = width - this.pnode.getPadding("lr");
30346         this.inner.setWidth(iwidth);
30347         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30348         this.pnode.setWidth(width);
30349     },
30350
30351     /**
30352      * Show or hide the tab
30353      * @param {Boolean} hidden True to hide or false to show.
30354      */
30355     setHidden : function(hidden){
30356         this.hidden = hidden;
30357         this.pnode.setStyle("display", hidden ? "none" : "");
30358     },
30359
30360     /**
30361      * Returns true if this tab is "hidden"
30362      * @return {Boolean}
30363      */
30364     isHidden : function(){
30365         return this.hidden;
30366     },
30367
30368     /**
30369      * Returns the text for this tab
30370      * @return {String}
30371      */
30372     getText : function(){
30373         return this.text;
30374     },
30375
30376     autoSize : function(){
30377         //this.el.beginMeasure();
30378         this.textEl.setWidth(1);
30379         /*
30380          *  #2804 [new] Tabs in Roojs
30381          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30382          */
30383         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30384         //this.el.endMeasure();
30385     },
30386
30387     /**
30388      * Sets the text for the tab (Note: this also sets the tooltip text)
30389      * @param {String} text The tab's text and tooltip
30390      */
30391     setText : function(text){
30392         this.text = text;
30393         this.textEl.update(text);
30394         this.setTooltip(text);
30395         if(!this.tabPanel.resizeTabs){
30396             this.autoSize();
30397         }
30398     },
30399     /**
30400      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30401      */
30402     activate : function(){
30403         this.tabPanel.activate(this.id);
30404     },
30405
30406     /**
30407      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30408      */
30409     disable : function(){
30410         if(this.tabPanel.active != this){
30411             this.disabled = true;
30412             this.pnode.addClass("disabled");
30413         }
30414     },
30415
30416     /**
30417      * Enables this TabPanelItem if it was previously disabled.
30418      */
30419     enable : function(){
30420         this.disabled = false;
30421         this.pnode.removeClass("disabled");
30422     },
30423
30424     /**
30425      * Sets the content for this TabPanelItem.
30426      * @param {String} content The content
30427      * @param {Boolean} loadScripts true to look for and load scripts
30428      */
30429     setContent : function(content, loadScripts){
30430         this.bodyEl.update(content, loadScripts);
30431     },
30432
30433     /**
30434      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30435      * @return {Roo.UpdateManager} The UpdateManager
30436      */
30437     getUpdateManager : function(){
30438         return this.bodyEl.getUpdateManager();
30439     },
30440
30441     /**
30442      * Set a URL to be used to load the content for this TabPanelItem.
30443      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30444      * @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)
30445      * @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)
30446      * @return {Roo.UpdateManager} The UpdateManager
30447      */
30448     setUrl : function(url, params, loadOnce){
30449         if(this.refreshDelegate){
30450             this.un('activate', this.refreshDelegate);
30451         }
30452         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30453         this.on("activate", this.refreshDelegate);
30454         return this.bodyEl.getUpdateManager();
30455     },
30456
30457     /** @private */
30458     _handleRefresh : function(url, params, loadOnce){
30459         if(!loadOnce || !this.loaded){
30460             var updater = this.bodyEl.getUpdateManager();
30461             updater.update(url, params, this._setLoaded.createDelegate(this));
30462         }
30463     },
30464
30465     /**
30466      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30467      *   Will fail silently if the setUrl method has not been called.
30468      *   This does not activate the panel, just updates its content.
30469      */
30470     refresh : function(){
30471         if(this.refreshDelegate){
30472            this.loaded = false;
30473            this.refreshDelegate();
30474         }
30475     },
30476
30477     /** @private */
30478     _setLoaded : function(){
30479         this.loaded = true;
30480     },
30481
30482     /** @private */
30483     closeClick : function(e){
30484         var o = {};
30485         e.stopEvent();
30486         this.fireEvent("beforeclose", this, o);
30487         if(o.cancel !== true){
30488             this.tabPanel.removeTab(this.id);
30489         }
30490     },
30491     /**
30492      * The text displayed in the tooltip for the close icon.
30493      * @type String
30494      */
30495     closeText : "Close this tab"
30496 });
30497
30498 /** @private */
30499 Roo.TabPanel.prototype.createStrip = function(container){
30500     var strip = document.createElement("div");
30501     strip.className = "x-tabs-wrap";
30502     container.appendChild(strip);
30503     return strip;
30504 };
30505 /** @private */
30506 Roo.TabPanel.prototype.createStripList = function(strip){
30507     // div wrapper for retard IE
30508     // returns the "tr" element.
30509     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30510         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30511         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30512     return strip.firstChild.firstChild.firstChild.firstChild;
30513 };
30514 /** @private */
30515 Roo.TabPanel.prototype.createBody = function(container){
30516     var body = document.createElement("div");
30517     Roo.id(body, "tab-body");
30518     Roo.fly(body).addClass("x-tabs-body");
30519     container.appendChild(body);
30520     return body;
30521 };
30522 /** @private */
30523 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30524     var body = Roo.getDom(id);
30525     if(!body){
30526         body = document.createElement("div");
30527         body.id = id;
30528     }
30529     Roo.fly(body).addClass("x-tabs-item-body");
30530     bodyEl.insertBefore(body, bodyEl.firstChild);
30531     return body;
30532 };
30533 /** @private */
30534 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30535     var td = document.createElement("td");
30536     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30537     //stripEl.appendChild(td);
30538     if(closable){
30539         td.className = "x-tabs-closable";
30540         if(!this.closeTpl){
30541             this.closeTpl = new Roo.Template(
30542                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30543                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30544                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30545             );
30546         }
30547         var el = this.closeTpl.overwrite(td, {"text": text});
30548         var close = el.getElementsByTagName("div")[0];
30549         var inner = el.getElementsByTagName("em")[0];
30550         return {"el": el, "close": close, "inner": inner};
30551     } else {
30552         if(!this.tabTpl){
30553             this.tabTpl = new Roo.Template(
30554                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30555                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30556             );
30557         }
30558         var el = this.tabTpl.overwrite(td, {"text": text});
30559         var inner = el.getElementsByTagName("em")[0];
30560         return {"el": el, "inner": inner};
30561     }
30562 };/*
30563  * Based on:
30564  * Ext JS Library 1.1.1
30565  * Copyright(c) 2006-2007, Ext JS, LLC.
30566  *
30567  * Originally Released Under LGPL - original licence link has changed is not relivant.
30568  *
30569  * Fork - LGPL
30570  * <script type="text/javascript">
30571  */
30572
30573 /**
30574  * @class Roo.Button
30575  * @extends Roo.util.Observable
30576  * Simple Button class
30577  * @cfg {String} text The button text
30578  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30579  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30580  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30581  * @cfg {Object} scope The scope of the handler
30582  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30583  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30584  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30585  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30586  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30587  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30588    applies if enableToggle = true)
30589  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30590  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30591   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30592  * @constructor
30593  * Create a new button
30594  * @param {Object} config The config object
30595  */
30596 Roo.Button = function(renderTo, config)
30597 {
30598     if (!config) {
30599         config = renderTo;
30600         renderTo = config.renderTo || false;
30601     }
30602     
30603     Roo.apply(this, config);
30604     this.addEvents({
30605         /**
30606              * @event click
30607              * Fires when this button is clicked
30608              * @param {Button} this
30609              * @param {EventObject} e The click event
30610              */
30611             "click" : true,
30612         /**
30613              * @event toggle
30614              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30615              * @param {Button} this
30616              * @param {Boolean} pressed
30617              */
30618             "toggle" : true,
30619         /**
30620              * @event mouseover
30621              * Fires when the mouse hovers over the button
30622              * @param {Button} this
30623              * @param {Event} e The event object
30624              */
30625         'mouseover' : true,
30626         /**
30627              * @event mouseout
30628              * Fires when the mouse exits the button
30629              * @param {Button} this
30630              * @param {Event} e The event object
30631              */
30632         'mouseout': true,
30633          /**
30634              * @event render
30635              * Fires when the button is rendered
30636              * @param {Button} this
30637              */
30638         'render': true
30639     });
30640     if(this.menu){
30641         this.menu = Roo.menu.MenuMgr.get(this.menu);
30642     }
30643     // register listeners first!!  - so render can be captured..
30644     Roo.util.Observable.call(this);
30645     if(renderTo){
30646         this.render(renderTo);
30647     }
30648     
30649   
30650 };
30651
30652 Roo.extend(Roo.Button, Roo.util.Observable, {
30653     /**
30654      * 
30655      */
30656     
30657     /**
30658      * Read-only. True if this button is hidden
30659      * @type Boolean
30660      */
30661     hidden : false,
30662     /**
30663      * Read-only. True if this button is disabled
30664      * @type Boolean
30665      */
30666     disabled : false,
30667     /**
30668      * Read-only. True if this button is pressed (only if enableToggle = true)
30669      * @type Boolean
30670      */
30671     pressed : false,
30672
30673     /**
30674      * @cfg {Number} tabIndex 
30675      * The DOM tabIndex for this button (defaults to undefined)
30676      */
30677     tabIndex : undefined,
30678
30679     /**
30680      * @cfg {Boolean} enableToggle
30681      * True to enable pressed/not pressed toggling (defaults to false)
30682      */
30683     enableToggle: false,
30684     /**
30685      * @cfg {Roo.menu.Menu} menu
30686      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30687      */
30688     menu : undefined,
30689     /**
30690      * @cfg {String} menuAlign
30691      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30692      */
30693     menuAlign : "tl-bl?",
30694
30695     /**
30696      * @cfg {String} iconCls
30697      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30698      */
30699     iconCls : undefined,
30700     /**
30701      * @cfg {String} type
30702      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30703      */
30704     type : 'button',
30705
30706     // private
30707     menuClassTarget: 'tr',
30708
30709     /**
30710      * @cfg {String} clickEvent
30711      * The type of event to map to the button's event handler (defaults to 'click')
30712      */
30713     clickEvent : 'click',
30714
30715     /**
30716      * @cfg {Boolean} handleMouseEvents
30717      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30718      */
30719     handleMouseEvents : true,
30720
30721     /**
30722      * @cfg {String} tooltipType
30723      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30724      */
30725     tooltipType : 'qtip',
30726
30727     /**
30728      * @cfg {String} cls
30729      * A CSS class to apply to the button's main element.
30730      */
30731     
30732     /**
30733      * @cfg {Roo.Template} template (Optional)
30734      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30735      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30736      * require code modifications if required elements (e.g. a button) aren't present.
30737      */
30738
30739     // private
30740     render : function(renderTo){
30741         var btn;
30742         if(this.hideParent){
30743             this.parentEl = Roo.get(renderTo);
30744         }
30745         if(!this.dhconfig){
30746             if(!this.template){
30747                 if(!Roo.Button.buttonTemplate){
30748                     // hideous table template
30749                     Roo.Button.buttonTemplate = new Roo.Template(
30750                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30751                         '<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>',
30752                         "</tr></tbody></table>");
30753                 }
30754                 this.template = Roo.Button.buttonTemplate;
30755             }
30756             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30757             var btnEl = btn.child("button:first");
30758             btnEl.on('focus', this.onFocus, this);
30759             btnEl.on('blur', this.onBlur, this);
30760             if(this.cls){
30761                 btn.addClass(this.cls);
30762             }
30763             if(this.icon){
30764                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30765             }
30766             if(this.iconCls){
30767                 btnEl.addClass(this.iconCls);
30768                 if(!this.cls){
30769                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30770                 }
30771             }
30772             if(this.tabIndex !== undefined){
30773                 btnEl.dom.tabIndex = this.tabIndex;
30774             }
30775             if(this.tooltip){
30776                 if(typeof this.tooltip == 'object'){
30777                     Roo.QuickTips.tips(Roo.apply({
30778                           target: btnEl.id
30779                     }, this.tooltip));
30780                 } else {
30781                     btnEl.dom[this.tooltipType] = this.tooltip;
30782                 }
30783             }
30784         }else{
30785             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30786         }
30787         this.el = btn;
30788         if(this.id){
30789             this.el.dom.id = this.el.id = this.id;
30790         }
30791         if(this.menu){
30792             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30793             this.menu.on("show", this.onMenuShow, this);
30794             this.menu.on("hide", this.onMenuHide, this);
30795         }
30796         btn.addClass("x-btn");
30797         if(Roo.isIE && !Roo.isIE7){
30798             this.autoWidth.defer(1, this);
30799         }else{
30800             this.autoWidth();
30801         }
30802         if(this.handleMouseEvents){
30803             btn.on("mouseover", this.onMouseOver, this);
30804             btn.on("mouseout", this.onMouseOut, this);
30805             btn.on("mousedown", this.onMouseDown, this);
30806         }
30807         btn.on(this.clickEvent, this.onClick, this);
30808         //btn.on("mouseup", this.onMouseUp, this);
30809         if(this.hidden){
30810             this.hide();
30811         }
30812         if(this.disabled){
30813             this.disable();
30814         }
30815         Roo.ButtonToggleMgr.register(this);
30816         if(this.pressed){
30817             this.el.addClass("x-btn-pressed");
30818         }
30819         if(this.repeat){
30820             var repeater = new Roo.util.ClickRepeater(btn,
30821                 typeof this.repeat == "object" ? this.repeat : {}
30822             );
30823             repeater.on("click", this.onClick,  this);
30824         }
30825         
30826         this.fireEvent('render', this);
30827         
30828     },
30829     /**
30830      * Returns the button's underlying element
30831      * @return {Roo.Element} The element
30832      */
30833     getEl : function(){
30834         return this.el;  
30835     },
30836     
30837     /**
30838      * Destroys this Button and removes any listeners.
30839      */
30840     destroy : function(){
30841         Roo.ButtonToggleMgr.unregister(this);
30842         this.el.removeAllListeners();
30843         this.purgeListeners();
30844         this.el.remove();
30845     },
30846
30847     // private
30848     autoWidth : function(){
30849         if(this.el){
30850             this.el.setWidth("auto");
30851             if(Roo.isIE7 && Roo.isStrict){
30852                 var ib = this.el.child('button');
30853                 if(ib && ib.getWidth() > 20){
30854                     ib.clip();
30855                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30856                 }
30857             }
30858             if(this.minWidth){
30859                 if(this.hidden){
30860                     this.el.beginMeasure();
30861                 }
30862                 if(this.el.getWidth() < this.minWidth){
30863                     this.el.setWidth(this.minWidth);
30864                 }
30865                 if(this.hidden){
30866                     this.el.endMeasure();
30867                 }
30868             }
30869         }
30870     },
30871
30872     /**
30873      * Assigns this button's click handler
30874      * @param {Function} handler The function to call when the button is clicked
30875      * @param {Object} scope (optional) Scope for the function passed in
30876      */
30877     setHandler : function(handler, scope){
30878         this.handler = handler;
30879         this.scope = scope;  
30880     },
30881     
30882     /**
30883      * Sets this button's text
30884      * @param {String} text The button text
30885      */
30886     setText : function(text){
30887         this.text = text;
30888         if(this.el){
30889             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30890         }
30891         this.autoWidth();
30892     },
30893     
30894     /**
30895      * Gets the text for this button
30896      * @return {String} The button text
30897      */
30898     getText : function(){
30899         return this.text;  
30900     },
30901     
30902     /**
30903      * Show this button
30904      */
30905     show: function(){
30906         this.hidden = false;
30907         if(this.el){
30908             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30909         }
30910     },
30911     
30912     /**
30913      * Hide this button
30914      */
30915     hide: function(){
30916         this.hidden = true;
30917         if(this.el){
30918             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30919         }
30920     },
30921     
30922     /**
30923      * Convenience function for boolean show/hide
30924      * @param {Boolean} visible True to show, false to hide
30925      */
30926     setVisible: function(visible){
30927         if(visible) {
30928             this.show();
30929         }else{
30930             this.hide();
30931         }
30932     },
30933     
30934     /**
30935      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30936      * @param {Boolean} state (optional) Force a particular state
30937      */
30938     toggle : function(state){
30939         state = state === undefined ? !this.pressed : state;
30940         if(state != this.pressed){
30941             if(state){
30942                 this.el.addClass("x-btn-pressed");
30943                 this.pressed = true;
30944                 this.fireEvent("toggle", this, true);
30945             }else{
30946                 this.el.removeClass("x-btn-pressed");
30947                 this.pressed = false;
30948                 this.fireEvent("toggle", this, false);
30949             }
30950             if(this.toggleHandler){
30951                 this.toggleHandler.call(this.scope || this, this, state);
30952             }
30953         }
30954     },
30955     
30956     /**
30957      * Focus the button
30958      */
30959     focus : function(){
30960         this.el.child('button:first').focus();
30961     },
30962     
30963     /**
30964      * Disable this button
30965      */
30966     disable : function(){
30967         if(this.el){
30968             this.el.addClass("x-btn-disabled");
30969         }
30970         this.disabled = true;
30971     },
30972     
30973     /**
30974      * Enable this button
30975      */
30976     enable : function(){
30977         if(this.el){
30978             this.el.removeClass("x-btn-disabled");
30979         }
30980         this.disabled = false;
30981     },
30982
30983     /**
30984      * Convenience function for boolean enable/disable
30985      * @param {Boolean} enabled True to enable, false to disable
30986      */
30987     setDisabled : function(v){
30988         this[v !== true ? "enable" : "disable"]();
30989     },
30990
30991     // private
30992     onClick : function(e)
30993     {
30994         if(e){
30995             e.preventDefault();
30996         }
30997         if(e.button != 0){
30998             return;
30999         }
31000         if(!this.disabled){
31001             if(this.enableToggle){
31002                 this.toggle();
31003             }
31004             if(this.menu && !this.menu.isVisible()){
31005                 this.menu.show(this.el, this.menuAlign);
31006             }
31007             this.fireEvent("click", this, e);
31008             if(this.handler){
31009                 this.el.removeClass("x-btn-over");
31010                 this.handler.call(this.scope || this, this, e);
31011             }
31012         }
31013     },
31014     // private
31015     onMouseOver : function(e){
31016         if(!this.disabled){
31017             this.el.addClass("x-btn-over");
31018             this.fireEvent('mouseover', this, e);
31019         }
31020     },
31021     // private
31022     onMouseOut : function(e){
31023         if(!e.within(this.el,  true)){
31024             this.el.removeClass("x-btn-over");
31025             this.fireEvent('mouseout', this, e);
31026         }
31027     },
31028     // private
31029     onFocus : function(e){
31030         if(!this.disabled){
31031             this.el.addClass("x-btn-focus");
31032         }
31033     },
31034     // private
31035     onBlur : function(e){
31036         this.el.removeClass("x-btn-focus");
31037     },
31038     // private
31039     onMouseDown : function(e){
31040         if(!this.disabled && e.button == 0){
31041             this.el.addClass("x-btn-click");
31042             Roo.get(document).on('mouseup', this.onMouseUp, this);
31043         }
31044     },
31045     // private
31046     onMouseUp : function(e){
31047         if(e.button == 0){
31048             this.el.removeClass("x-btn-click");
31049             Roo.get(document).un('mouseup', this.onMouseUp, this);
31050         }
31051     },
31052     // private
31053     onMenuShow : function(e){
31054         this.el.addClass("x-btn-menu-active");
31055     },
31056     // private
31057     onMenuHide : function(e){
31058         this.el.removeClass("x-btn-menu-active");
31059     }   
31060 });
31061
31062 // Private utility class used by Button
31063 Roo.ButtonToggleMgr = function(){
31064    var groups = {};
31065    
31066    function toggleGroup(btn, state){
31067        if(state){
31068            var g = groups[btn.toggleGroup];
31069            for(var i = 0, l = g.length; i < l; i++){
31070                if(g[i] != btn){
31071                    g[i].toggle(false);
31072                }
31073            }
31074        }
31075    }
31076    
31077    return {
31078        register : function(btn){
31079            if(!btn.toggleGroup){
31080                return;
31081            }
31082            var g = groups[btn.toggleGroup];
31083            if(!g){
31084                g = groups[btn.toggleGroup] = [];
31085            }
31086            g.push(btn);
31087            btn.on("toggle", toggleGroup);
31088        },
31089        
31090        unregister : function(btn){
31091            if(!btn.toggleGroup){
31092                return;
31093            }
31094            var g = groups[btn.toggleGroup];
31095            if(g){
31096                g.remove(btn);
31097                btn.un("toggle", toggleGroup);
31098            }
31099        }
31100    };
31101 }();/*
31102  * Based on:
31103  * Ext JS Library 1.1.1
31104  * Copyright(c) 2006-2007, Ext JS, LLC.
31105  *
31106  * Originally Released Under LGPL - original licence link has changed is not relivant.
31107  *
31108  * Fork - LGPL
31109  * <script type="text/javascript">
31110  */
31111  
31112 /**
31113  * @class Roo.SplitButton
31114  * @extends Roo.Button
31115  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
31116  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
31117  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
31118  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
31119  * @cfg {String} arrowTooltip The title attribute of the arrow
31120  * @constructor
31121  * Create a new menu button
31122  * @param {String/HTMLElement/Element} renderTo The element to append the button to
31123  * @param {Object} config The config object
31124  */
31125 Roo.SplitButton = function(renderTo, config){
31126     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
31127     /**
31128      * @event arrowclick
31129      * Fires when this button's arrow is clicked
31130      * @param {SplitButton} this
31131      * @param {EventObject} e The click event
31132      */
31133     this.addEvents({"arrowclick":true});
31134 };
31135
31136 Roo.extend(Roo.SplitButton, Roo.Button, {
31137     render : function(renderTo){
31138         // this is one sweet looking template!
31139         var tpl = new Roo.Template(
31140             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
31141             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
31142             '<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>',
31143             "</tbody></table></td><td>",
31144             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
31145             '<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>',
31146             "</tbody></table></td></tr></table>"
31147         );
31148         var btn = tpl.append(renderTo, [this.text, this.type], true);
31149         var btnEl = btn.child("button");
31150         if(this.cls){
31151             btn.addClass(this.cls);
31152         }
31153         if(this.icon){
31154             btnEl.setStyle('background-image', 'url(' +this.icon +')');
31155         }
31156         if(this.iconCls){
31157             btnEl.addClass(this.iconCls);
31158             if(!this.cls){
31159                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
31160             }
31161         }
31162         this.el = btn;
31163         if(this.handleMouseEvents){
31164             btn.on("mouseover", this.onMouseOver, this);
31165             btn.on("mouseout", this.onMouseOut, this);
31166             btn.on("mousedown", this.onMouseDown, this);
31167             btn.on("mouseup", this.onMouseUp, this);
31168         }
31169         btn.on(this.clickEvent, this.onClick, this);
31170         if(this.tooltip){
31171             if(typeof this.tooltip == 'object'){
31172                 Roo.QuickTips.tips(Roo.apply({
31173                       target: btnEl.id
31174                 }, this.tooltip));
31175             } else {
31176                 btnEl.dom[this.tooltipType] = this.tooltip;
31177             }
31178         }
31179         if(this.arrowTooltip){
31180             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
31181         }
31182         if(this.hidden){
31183             this.hide();
31184         }
31185         if(this.disabled){
31186             this.disable();
31187         }
31188         if(this.pressed){
31189             this.el.addClass("x-btn-pressed");
31190         }
31191         if(Roo.isIE && !Roo.isIE7){
31192             this.autoWidth.defer(1, this);
31193         }else{
31194             this.autoWidth();
31195         }
31196         if(this.menu){
31197             this.menu.on("show", this.onMenuShow, this);
31198             this.menu.on("hide", this.onMenuHide, this);
31199         }
31200         this.fireEvent('render', this);
31201     },
31202
31203     // private
31204     autoWidth : function(){
31205         if(this.el){
31206             var tbl = this.el.child("table:first");
31207             var tbl2 = this.el.child("table:last");
31208             this.el.setWidth("auto");
31209             tbl.setWidth("auto");
31210             if(Roo.isIE7 && Roo.isStrict){
31211                 var ib = this.el.child('button:first');
31212                 if(ib && ib.getWidth() > 20){
31213                     ib.clip();
31214                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
31215                 }
31216             }
31217             if(this.minWidth){
31218                 if(this.hidden){
31219                     this.el.beginMeasure();
31220                 }
31221                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
31222                     tbl.setWidth(this.minWidth-tbl2.getWidth());
31223                 }
31224                 if(this.hidden){
31225                     this.el.endMeasure();
31226                 }
31227             }
31228             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
31229         } 
31230     },
31231     /**
31232      * Sets this button's click handler
31233      * @param {Function} handler The function to call when the button is clicked
31234      * @param {Object} scope (optional) Scope for the function passed above
31235      */
31236     setHandler : function(handler, scope){
31237         this.handler = handler;
31238         this.scope = scope;  
31239     },
31240     
31241     /**
31242      * Sets this button's arrow click handler
31243      * @param {Function} handler The function to call when the arrow is clicked
31244      * @param {Object} scope (optional) Scope for the function passed above
31245      */
31246     setArrowHandler : function(handler, scope){
31247         this.arrowHandler = handler;
31248         this.scope = scope;  
31249     },
31250     
31251     /**
31252      * Focus the button
31253      */
31254     focus : function(){
31255         if(this.el){
31256             this.el.child("button:first").focus();
31257         }
31258     },
31259
31260     // private
31261     onClick : function(e){
31262         e.preventDefault();
31263         if(!this.disabled){
31264             if(e.getTarget(".x-btn-menu-arrow-wrap")){
31265                 if(this.menu && !this.menu.isVisible()){
31266                     this.menu.show(this.el, this.menuAlign);
31267                 }
31268                 this.fireEvent("arrowclick", this, e);
31269                 if(this.arrowHandler){
31270                     this.arrowHandler.call(this.scope || this, this, e);
31271                 }
31272             }else{
31273                 this.fireEvent("click", this, e);
31274                 if(this.handler){
31275                     this.handler.call(this.scope || this, this, e);
31276                 }
31277             }
31278         }
31279     },
31280     // private
31281     onMouseDown : function(e){
31282         if(!this.disabled){
31283             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
31284         }
31285     },
31286     // private
31287     onMouseUp : function(e){
31288         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
31289     }   
31290 });
31291
31292
31293 // backwards compat
31294 Roo.MenuButton = Roo.SplitButton;/*
31295  * Based on:
31296  * Ext JS Library 1.1.1
31297  * Copyright(c) 2006-2007, Ext JS, LLC.
31298  *
31299  * Originally Released Under LGPL - original licence link has changed is not relivant.
31300  *
31301  * Fork - LGPL
31302  * <script type="text/javascript">
31303  */
31304
31305 /**
31306  * @class Roo.Toolbar
31307  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field 
31308  * Basic Toolbar class.
31309  * @constructor
31310  * Creates a new Toolbar
31311  * @param {Object} container The config object
31312  */ 
31313 Roo.Toolbar = function(container, buttons, config)
31314 {
31315     /// old consturctor format still supported..
31316     if(container instanceof Array){ // omit the container for later rendering
31317         buttons = container;
31318         config = buttons;
31319         container = null;
31320     }
31321     if (typeof(container) == 'object' && container.xtype) {
31322         config = container;
31323         container = config.container;
31324         buttons = config.buttons || []; // not really - use items!!
31325     }
31326     var xitems = [];
31327     if (config && config.items) {
31328         xitems = config.items;
31329         delete config.items;
31330     }
31331     Roo.apply(this, config);
31332     this.buttons = buttons;
31333     
31334     if(container){
31335         this.render(container);
31336     }
31337     this.xitems = xitems;
31338     Roo.each(xitems, function(b) {
31339         this.add(b);
31340     }, this);
31341     
31342 };
31343
31344 Roo.Toolbar.prototype = {
31345     /**
31346      * @cfg {Array} items
31347      * array of button configs or elements to add (will be converted to a MixedCollection)
31348      */
31349     items: false,
31350     /**
31351      * @cfg {String/HTMLElement/Element} container
31352      * The id or element that will contain the toolbar
31353      */
31354     // private
31355     render : function(ct){
31356         this.el = Roo.get(ct);
31357         if(this.cls){
31358             this.el.addClass(this.cls);
31359         }
31360         // using a table allows for vertical alignment
31361         // 100% width is needed by Safari...
31362         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31363         this.tr = this.el.child("tr", true);
31364         var autoId = 0;
31365         this.items = new Roo.util.MixedCollection(false, function(o){
31366             return o.id || ("item" + (++autoId));
31367         });
31368         if(this.buttons){
31369             this.add.apply(this, this.buttons);
31370             delete this.buttons;
31371         }
31372     },
31373
31374     /**
31375      * Adds element(s) to the toolbar -- this function takes a variable number of 
31376      * arguments of mixed type and adds them to the toolbar.
31377      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31378      * <ul>
31379      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31380      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31381      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31382      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31383      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31384      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31385      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31386      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31387      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31388      * </ul>
31389      * @param {Mixed} arg2
31390      * @param {Mixed} etc.
31391      */
31392     add : function(){
31393         var a = arguments, l = a.length;
31394         for(var i = 0; i < l; i++){
31395             this._add(a[i]);
31396         }
31397     },
31398     // private..
31399     _add : function(el) {
31400         
31401         if (el.xtype) {
31402             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31403         }
31404         
31405         if (el.applyTo){ // some kind of form field
31406             return this.addField(el);
31407         } 
31408         if (el.render){ // some kind of Toolbar.Item
31409             return this.addItem(el);
31410         }
31411         if (typeof el == "string"){ // string
31412             if(el == "separator" || el == "-"){
31413                 return this.addSeparator();
31414             }
31415             if (el == " "){
31416                 return this.addSpacer();
31417             }
31418             if(el == "->"){
31419                 return this.addFill();
31420             }
31421             return this.addText(el);
31422             
31423         }
31424         if(el.tagName){ // element
31425             return this.addElement(el);
31426         }
31427         if(typeof el == "object"){ // must be button config?
31428             return this.addButton(el);
31429         }
31430         // and now what?!?!
31431         return false;
31432         
31433     },
31434     
31435     /**
31436      * Add an Xtype element
31437      * @param {Object} xtype Xtype Object
31438      * @return {Object} created Object
31439      */
31440     addxtype : function(e){
31441         return this.add(e);  
31442     },
31443     
31444     /**
31445      * Returns the Element for this toolbar.
31446      * @return {Roo.Element}
31447      */
31448     getEl : function(){
31449         return this.el;  
31450     },
31451     
31452     /**
31453      * Adds a separator
31454      * @return {Roo.Toolbar.Item} The separator item
31455      */
31456     addSeparator : function(){
31457         return this.addItem(new Roo.Toolbar.Separator());
31458     },
31459
31460     /**
31461      * Adds a spacer element
31462      * @return {Roo.Toolbar.Spacer} The spacer item
31463      */
31464     addSpacer : function(){
31465         return this.addItem(new Roo.Toolbar.Spacer());
31466     },
31467
31468     /**
31469      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31470      * @return {Roo.Toolbar.Fill} The fill item
31471      */
31472     addFill : function(){
31473         return this.addItem(new Roo.Toolbar.Fill());
31474     },
31475
31476     /**
31477      * Adds any standard HTML element to the toolbar
31478      * @param {String/HTMLElement/Element} el The element or id of the element to add
31479      * @return {Roo.Toolbar.Item} The element's item
31480      */
31481     addElement : function(el){
31482         return this.addItem(new Roo.Toolbar.Item(el));
31483     },
31484     /**
31485      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31486      * @type Roo.util.MixedCollection  
31487      */
31488     items : false,
31489      
31490     /**
31491      * Adds any Toolbar.Item or subclass
31492      * @param {Roo.Toolbar.Item} item
31493      * @return {Roo.Toolbar.Item} The item
31494      */
31495     addItem : function(item){
31496         var td = this.nextBlock();
31497         item.render(td);
31498         this.items.add(item);
31499         return item;
31500     },
31501     
31502     /**
31503      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31504      * @param {Object/Array} config A button config or array of configs
31505      * @return {Roo.Toolbar.Button/Array}
31506      */
31507     addButton : function(config){
31508         if(config instanceof Array){
31509             var buttons = [];
31510             for(var i = 0, len = config.length; i < len; i++) {
31511                 buttons.push(this.addButton(config[i]));
31512             }
31513             return buttons;
31514         }
31515         var b = config;
31516         if(!(config instanceof Roo.Toolbar.Button)){
31517             b = config.split ?
31518                 new Roo.Toolbar.SplitButton(config) :
31519                 new Roo.Toolbar.Button(config);
31520         }
31521         var td = this.nextBlock();
31522         b.render(td);
31523         this.items.add(b);
31524         return b;
31525     },
31526     
31527     /**
31528      * Adds text to the toolbar
31529      * @param {String} text The text to add
31530      * @return {Roo.Toolbar.Item} The element's item
31531      */
31532     addText : function(text){
31533         return this.addItem(new Roo.Toolbar.TextItem(text));
31534     },
31535     
31536     /**
31537      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31538      * @param {Number} index The index where the item is to be inserted
31539      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31540      * @return {Roo.Toolbar.Button/Item}
31541      */
31542     insertButton : function(index, item){
31543         if(item instanceof Array){
31544             var buttons = [];
31545             for(var i = 0, len = item.length; i < len; i++) {
31546                buttons.push(this.insertButton(index + i, item[i]));
31547             }
31548             return buttons;
31549         }
31550         if (!(item instanceof Roo.Toolbar.Button)){
31551            item = new Roo.Toolbar.Button(item);
31552         }
31553         var td = document.createElement("td");
31554         this.tr.insertBefore(td, this.tr.childNodes[index]);
31555         item.render(td);
31556         this.items.insert(index, item);
31557         return item;
31558     },
31559     
31560     /**
31561      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31562      * @param {Object} config
31563      * @return {Roo.Toolbar.Item} The element's item
31564      */
31565     addDom : function(config, returnEl){
31566         var td = this.nextBlock();
31567         Roo.DomHelper.overwrite(td, config);
31568         var ti = new Roo.Toolbar.Item(td.firstChild);
31569         ti.render(td);
31570         this.items.add(ti);
31571         return ti;
31572     },
31573
31574     /**
31575      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31576      * @type Roo.util.MixedCollection  
31577      */
31578     fields : false,
31579     
31580     /**
31581      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31582      * Note: the field should not have been rendered yet. For a field that has already been
31583      * rendered, use {@link #addElement}.
31584      * @param {Roo.form.Field} field
31585      * @return {Roo.ToolbarItem}
31586      */
31587      
31588       
31589     addField : function(field) {
31590         if (!this.fields) {
31591             var autoId = 0;
31592             this.fields = new Roo.util.MixedCollection(false, function(o){
31593                 return o.id || ("item" + (++autoId));
31594             });
31595
31596         }
31597         
31598         var td = this.nextBlock();
31599         field.render(td);
31600         var ti = new Roo.Toolbar.Item(td.firstChild);
31601         ti.render(td);
31602         this.items.add(ti);
31603         this.fields.add(field);
31604         return ti;
31605     },
31606     /**
31607      * Hide the toolbar
31608      * @method hide
31609      */
31610      
31611       
31612     hide : function()
31613     {
31614         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31615         this.el.child('div').hide();
31616     },
31617     /**
31618      * Show the toolbar
31619      * @method show
31620      */
31621     show : function()
31622     {
31623         this.el.child('div').show();
31624     },
31625       
31626     // private
31627     nextBlock : function(){
31628         var td = document.createElement("td");
31629         this.tr.appendChild(td);
31630         return td;
31631     },
31632
31633     // private
31634     destroy : function(){
31635         if(this.items){ // rendered?
31636             Roo.destroy.apply(Roo, this.items.items);
31637         }
31638         if(this.fields){ // rendered?
31639             Roo.destroy.apply(Roo, this.fields.items);
31640         }
31641         Roo.Element.uncache(this.el, this.tr);
31642     }
31643 };
31644
31645 /**
31646  * @class Roo.Toolbar.Item
31647  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31648  * @constructor
31649  * Creates a new Item
31650  * @param {HTMLElement} el 
31651  */
31652 Roo.Toolbar.Item = function(el){
31653     var cfg = {};
31654     if (typeof (el.xtype) != 'undefined') {
31655         cfg = el;
31656         el = cfg.el;
31657     }
31658     
31659     this.el = Roo.getDom(el);
31660     this.id = Roo.id(this.el);
31661     this.hidden = false;
31662     
31663     this.addEvents({
31664          /**
31665              * @event render
31666              * Fires when the button is rendered
31667              * @param {Button} this
31668              */
31669         'render': true
31670     });
31671     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31672 };
31673 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31674 //Roo.Toolbar.Item.prototype = {
31675     
31676     /**
31677      * Get this item's HTML Element
31678      * @return {HTMLElement}
31679      */
31680     getEl : function(){
31681        return this.el;  
31682     },
31683
31684     // private
31685     render : function(td){
31686         
31687          this.td = td;
31688         td.appendChild(this.el);
31689         
31690         this.fireEvent('render', this);
31691     },
31692     
31693     /**
31694      * Removes and destroys this item.
31695      */
31696     destroy : function(){
31697         this.td.parentNode.removeChild(this.td);
31698     },
31699     
31700     /**
31701      * Shows this item.
31702      */
31703     show: function(){
31704         this.hidden = false;
31705         this.td.style.display = "";
31706     },
31707     
31708     /**
31709      * Hides this item.
31710      */
31711     hide: function(){
31712         this.hidden = true;
31713         this.td.style.display = "none";
31714     },
31715     
31716     /**
31717      * Convenience function for boolean show/hide.
31718      * @param {Boolean} visible true to show/false to hide
31719      */
31720     setVisible: function(visible){
31721         if(visible) {
31722             this.show();
31723         }else{
31724             this.hide();
31725         }
31726     },
31727     
31728     /**
31729      * Try to focus this item.
31730      */
31731     focus : function(){
31732         Roo.fly(this.el).focus();
31733     },
31734     
31735     /**
31736      * Disables this item.
31737      */
31738     disable : function(){
31739         Roo.fly(this.td).addClass("x-item-disabled");
31740         this.disabled = true;
31741         this.el.disabled = true;
31742     },
31743     
31744     /**
31745      * Enables this item.
31746      */
31747     enable : function(){
31748         Roo.fly(this.td).removeClass("x-item-disabled");
31749         this.disabled = false;
31750         this.el.disabled = false;
31751     }
31752 });
31753
31754
31755 /**
31756  * @class Roo.Toolbar.Separator
31757  * @extends Roo.Toolbar.Item
31758  * A simple toolbar separator class
31759  * @constructor
31760  * Creates a new Separator
31761  */
31762 Roo.Toolbar.Separator = function(cfg){
31763     
31764     var s = document.createElement("span");
31765     s.className = "ytb-sep";
31766     if (cfg) {
31767         cfg.el = s;
31768     }
31769     
31770     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31771 };
31772 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31773     enable:Roo.emptyFn,
31774     disable:Roo.emptyFn,
31775     focus:Roo.emptyFn
31776 });
31777
31778 /**
31779  * @class Roo.Toolbar.Spacer
31780  * @extends Roo.Toolbar.Item
31781  * A simple element that adds extra horizontal space to a toolbar.
31782  * @constructor
31783  * Creates a new Spacer
31784  */
31785 Roo.Toolbar.Spacer = function(cfg){
31786     var s = document.createElement("div");
31787     s.className = "ytb-spacer";
31788     if (cfg) {
31789         cfg.el = s;
31790     }
31791     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31792 };
31793 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31794     enable:Roo.emptyFn,
31795     disable:Roo.emptyFn,
31796     focus:Roo.emptyFn
31797 });
31798
31799 /**
31800  * @class Roo.Toolbar.Fill
31801  * @extends Roo.Toolbar.Spacer
31802  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31803  * @constructor
31804  * Creates a new Spacer
31805  */
31806 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31807     // private
31808     render : function(td){
31809         td.style.width = '100%';
31810         Roo.Toolbar.Fill.superclass.render.call(this, td);
31811     }
31812 });
31813
31814 /**
31815  * @class Roo.Toolbar.TextItem
31816  * @extends Roo.Toolbar.Item
31817  * A simple class that renders text directly into a toolbar.
31818  * @constructor
31819  * Creates a new TextItem
31820  * @cfg {string} text 
31821  */
31822 Roo.Toolbar.TextItem = function(cfg){
31823     var  text = cfg || "";
31824     if (typeof(cfg) == 'object') {
31825         text = cfg.text || "";
31826     }  else {
31827         cfg = null;
31828     }
31829     var s = document.createElement("span");
31830     s.className = "ytb-text";
31831     s.innerHTML = text;
31832     if (cfg) {
31833         cfg.el  = s;
31834     }
31835     
31836     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31837 };
31838 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31839     
31840      
31841     enable:Roo.emptyFn,
31842     disable:Roo.emptyFn,
31843     focus:Roo.emptyFn,
31844      /**
31845      * Shows this button
31846      */
31847     show: function(){
31848         this.hidden = false;
31849         this.el.style.display = "";
31850     },
31851     
31852     /**
31853      * Hides this button
31854      */
31855     hide: function(){
31856         this.hidden = true;
31857         this.el.style.display = "none";
31858     }
31859     
31860 });
31861
31862 /**
31863  * @class Roo.Toolbar.Button
31864  * @extends Roo.Button
31865  * A button that renders into a toolbar.
31866  * @constructor
31867  * Creates a new Button
31868  * @param {Object} config A standard {@link Roo.Button} config object
31869  */
31870 Roo.Toolbar.Button = function(config){
31871     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31872 };
31873 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31874 {
31875     
31876     
31877     render : function(td){
31878         this.td = td;
31879         Roo.Toolbar.Button.superclass.render.call(this, td);
31880     },
31881     
31882     /**
31883      * Removes and destroys this button
31884      */
31885     destroy : function(){
31886         Roo.Toolbar.Button.superclass.destroy.call(this);
31887         this.td.parentNode.removeChild(this.td);
31888     },
31889     
31890     /**
31891      * Shows this button
31892      */
31893     show: function(){
31894         this.hidden = false;
31895         this.td.style.display = "";
31896     },
31897     
31898     /**
31899      * Hides this button
31900      */
31901     hide: function(){
31902         this.hidden = true;
31903         this.td.style.display = "none";
31904     },
31905
31906     /**
31907      * Disables this item
31908      */
31909     disable : function(){
31910         Roo.fly(this.td).addClass("x-item-disabled");
31911         this.disabled = true;
31912     },
31913
31914     /**
31915      * Enables this item
31916      */
31917     enable : function(){
31918         Roo.fly(this.td).removeClass("x-item-disabled");
31919         this.disabled = false;
31920     }
31921 });
31922 // backwards compat
31923 Roo.ToolbarButton = Roo.Toolbar.Button;
31924
31925 /**
31926  * @class Roo.Toolbar.SplitButton
31927  * @extends Roo.SplitButton
31928  * A menu button that renders into a toolbar.
31929  * @constructor
31930  * Creates a new SplitButton
31931  * @param {Object} config A standard {@link Roo.SplitButton} config object
31932  */
31933 Roo.Toolbar.SplitButton = function(config){
31934     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31935 };
31936 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31937     render : function(td){
31938         this.td = td;
31939         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31940     },
31941     
31942     /**
31943      * Removes and destroys this button
31944      */
31945     destroy : function(){
31946         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31947         this.td.parentNode.removeChild(this.td);
31948     },
31949     
31950     /**
31951      * Shows this button
31952      */
31953     show: function(){
31954         this.hidden = false;
31955         this.td.style.display = "";
31956     },
31957     
31958     /**
31959      * Hides this button
31960      */
31961     hide: function(){
31962         this.hidden = true;
31963         this.td.style.display = "none";
31964     }
31965 });
31966
31967 // backwards compat
31968 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31969  * Based on:
31970  * Ext JS Library 1.1.1
31971  * Copyright(c) 2006-2007, Ext JS, LLC.
31972  *
31973  * Originally Released Under LGPL - original licence link has changed is not relivant.
31974  *
31975  * Fork - LGPL
31976  * <script type="text/javascript">
31977  */
31978  
31979 /**
31980  * @class Roo.PagingToolbar
31981  * @extends Roo.Toolbar
31982  * @children   Roo.Toolbar.Item Roo.Toolbar.Button Roo.Toolbar.SplitButton Roo.form.Field
31983  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31984  * @constructor
31985  * Create a new PagingToolbar
31986  * @param {Object} config The config object
31987  */
31988 Roo.PagingToolbar = function(el, ds, config)
31989 {
31990     // old args format still supported... - xtype is prefered..
31991     if (typeof(el) == 'object' && el.xtype) {
31992         // created from xtype...
31993         config = el;
31994         ds = el.dataSource;
31995         el = config.container;
31996     }
31997     var items = [];
31998     if (config.items) {
31999         items = config.items;
32000         config.items = [];
32001     }
32002     
32003     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
32004     this.ds = ds;
32005     this.cursor = 0;
32006     this.renderButtons(this.el);
32007     this.bind(ds);
32008     
32009     // supprot items array.
32010    
32011     Roo.each(items, function(e) {
32012         this.add(Roo.factory(e));
32013     },this);
32014     
32015 };
32016
32017 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
32018    
32019     /**
32020      * @cfg {String/HTMLElement/Element} container
32021      * container The id or element that will contain the toolbar
32022      */
32023     /**
32024      * @cfg {Boolean} displayInfo
32025      * True to display the displayMsg (defaults to false)
32026      */
32027     
32028     
32029     /**
32030      * @cfg {Number} pageSize
32031      * The number of records to display per page (defaults to 20)
32032      */
32033     pageSize: 20,
32034     /**
32035      * @cfg {String} displayMsg
32036      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32037      */
32038     displayMsg : 'Displaying {0} - {1} of {2}',
32039     /**
32040      * @cfg {String} emptyMsg
32041      * The message to display when no records are found (defaults to "No data to display")
32042      */
32043     emptyMsg : 'No data to display',
32044     /**
32045      * Customizable piece of the default paging text (defaults to "Page")
32046      * @type String
32047      */
32048     beforePageText : "Page",
32049     /**
32050      * Customizable piece of the default paging text (defaults to "of %0")
32051      * @type String
32052      */
32053     afterPageText : "of {0}",
32054     /**
32055      * Customizable piece of the default paging text (defaults to "First Page")
32056      * @type String
32057      */
32058     firstText : "First Page",
32059     /**
32060      * Customizable piece of the default paging text (defaults to "Previous Page")
32061      * @type String
32062      */
32063     prevText : "Previous Page",
32064     /**
32065      * Customizable piece of the default paging text (defaults to "Next Page")
32066      * @type String
32067      */
32068     nextText : "Next Page",
32069     /**
32070      * Customizable piece of the default paging text (defaults to "Last Page")
32071      * @type String
32072      */
32073     lastText : "Last Page",
32074     /**
32075      * Customizable piece of the default paging text (defaults to "Refresh")
32076      * @type String
32077      */
32078     refreshText : "Refresh",
32079
32080     // private
32081     renderButtons : function(el){
32082         Roo.PagingToolbar.superclass.render.call(this, el);
32083         this.first = this.addButton({
32084             tooltip: this.firstText,
32085             cls: "x-btn-icon x-grid-page-first",
32086             disabled: true,
32087             handler: this.onClick.createDelegate(this, ["first"])
32088         });
32089         this.prev = this.addButton({
32090             tooltip: this.prevText,
32091             cls: "x-btn-icon x-grid-page-prev",
32092             disabled: true,
32093             handler: this.onClick.createDelegate(this, ["prev"])
32094         });
32095         //this.addSeparator();
32096         this.add(this.beforePageText);
32097         this.field = Roo.get(this.addDom({
32098            tag: "input",
32099            type: "text",
32100            size: "3",
32101            value: "1",
32102            cls: "x-grid-page-number"
32103         }).el);
32104         this.field.on("keydown", this.onPagingKeydown, this);
32105         this.field.on("focus", function(){this.dom.select();});
32106         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
32107         this.field.setHeight(18);
32108         //this.addSeparator();
32109         this.next = this.addButton({
32110             tooltip: this.nextText,
32111             cls: "x-btn-icon x-grid-page-next",
32112             disabled: true,
32113             handler: this.onClick.createDelegate(this, ["next"])
32114         });
32115         this.last = this.addButton({
32116             tooltip: this.lastText,
32117             cls: "x-btn-icon x-grid-page-last",
32118             disabled: true,
32119             handler: this.onClick.createDelegate(this, ["last"])
32120         });
32121         //this.addSeparator();
32122         this.loading = this.addButton({
32123             tooltip: this.refreshText,
32124             cls: "x-btn-icon x-grid-loading",
32125             handler: this.onClick.createDelegate(this, ["refresh"])
32126         });
32127
32128         if(this.displayInfo){
32129             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
32130         }
32131     },
32132
32133     // private
32134     updateInfo : function(){
32135         if(this.displayEl){
32136             var count = this.ds.getCount();
32137             var msg = count == 0 ?
32138                 this.emptyMsg :
32139                 String.format(
32140                     this.displayMsg,
32141                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
32142                 );
32143             this.displayEl.update(msg);
32144         }
32145     },
32146
32147     // private
32148     onLoad : function(ds, r, o){
32149        this.cursor = o.params ? o.params.start : 0;
32150        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
32151
32152        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
32153        this.field.dom.value = ap;
32154        this.first.setDisabled(ap == 1);
32155        this.prev.setDisabled(ap == 1);
32156        this.next.setDisabled(ap == ps);
32157        this.last.setDisabled(ap == ps);
32158        this.loading.enable();
32159        this.updateInfo();
32160     },
32161
32162     // private
32163     getPageData : function(){
32164         var total = this.ds.getTotalCount();
32165         return {
32166             total : total,
32167             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32168             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32169         };
32170     },
32171
32172     // private
32173     onLoadError : function(){
32174         this.loading.enable();
32175     },
32176
32177     // private
32178     onPagingKeydown : function(e){
32179         var k = e.getKey();
32180         var d = this.getPageData();
32181         if(k == e.RETURN){
32182             var v = this.field.dom.value, pageNum;
32183             if(!v || isNaN(pageNum = parseInt(v, 10))){
32184                 this.field.dom.value = d.activePage;
32185                 return;
32186             }
32187             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32188             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32189             e.stopEvent();
32190         }
32191         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))
32192         {
32193           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32194           this.field.dom.value = pageNum;
32195           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32196           e.stopEvent();
32197         }
32198         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32199         {
32200           var v = this.field.dom.value, pageNum; 
32201           var increment = (e.shiftKey) ? 10 : 1;
32202           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32203             increment *= -1;
32204           }
32205           if(!v || isNaN(pageNum = parseInt(v, 10))) {
32206             this.field.dom.value = d.activePage;
32207             return;
32208           }
32209           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32210           {
32211             this.field.dom.value = parseInt(v, 10) + increment;
32212             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32213             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32214           }
32215           e.stopEvent();
32216         }
32217     },
32218
32219     // private
32220     beforeLoad : function(){
32221         if(this.loading){
32222             this.loading.disable();
32223         }
32224     },
32225
32226     // private
32227     onClick : function(which){
32228         var ds = this.ds;
32229         switch(which){
32230             case "first":
32231                 ds.load({params:{start: 0, limit: this.pageSize}});
32232             break;
32233             case "prev":
32234                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
32235             break;
32236             case "next":
32237                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
32238             break;
32239             case "last":
32240                 var total = ds.getTotalCount();
32241                 var extra = total % this.pageSize;
32242                 var lastStart = extra ? (total - extra) : total-this.pageSize;
32243                 ds.load({params:{start: lastStart, limit: this.pageSize}});
32244             break;
32245             case "refresh":
32246                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
32247             break;
32248         }
32249     },
32250
32251     /**
32252      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
32253      * @param {Roo.data.Store} store The data store to unbind
32254      */
32255     unbind : function(ds){
32256         ds.un("beforeload", this.beforeLoad, this);
32257         ds.un("load", this.onLoad, this);
32258         ds.un("loadexception", this.onLoadError, this);
32259         ds.un("remove", this.updateInfo, this);
32260         ds.un("add", this.updateInfo, this);
32261         this.ds = undefined;
32262     },
32263
32264     /**
32265      * Binds the paging toolbar to the specified {@link Roo.data.Store}
32266      * @param {Roo.data.Store} store The data store to bind
32267      */
32268     bind : function(ds){
32269         ds.on("beforeload", this.beforeLoad, this);
32270         ds.on("load", this.onLoad, this);
32271         ds.on("loadexception", this.onLoadError, this);
32272         ds.on("remove", this.updateInfo, this);
32273         ds.on("add", this.updateInfo, this);
32274         this.ds = ds;
32275     }
32276 });/*
32277  * Based on:
32278  * Ext JS Library 1.1.1
32279  * Copyright(c) 2006-2007, Ext JS, LLC.
32280  *
32281  * Originally Released Under LGPL - original licence link has changed is not relivant.
32282  *
32283  * Fork - LGPL
32284  * <script type="text/javascript">
32285  */
32286
32287 /**
32288  * @class Roo.Resizable
32289  * @extends Roo.util.Observable
32290  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
32291  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
32292  * 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
32293  * the element will be wrapped for you automatically.</p>
32294  * <p>Here is the list of valid resize handles:</p>
32295  * <pre>
32296 Value   Description
32297 ------  -------------------
32298  'n'     north
32299  's'     south
32300  'e'     east
32301  'w'     west
32302  'nw'    northwest
32303  'sw'    southwest
32304  'se'    southeast
32305  'ne'    northeast
32306  'hd'    horizontal drag
32307  'all'   all
32308 </pre>
32309  * <p>Here's an example showing the creation of a typical Resizable:</p>
32310  * <pre><code>
32311 var resizer = new Roo.Resizable("element-id", {
32312     handles: 'all',
32313     minWidth: 200,
32314     minHeight: 100,
32315     maxWidth: 500,
32316     maxHeight: 400,
32317     pinned: true
32318 });
32319 resizer.on("resize", myHandler);
32320 </code></pre>
32321  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
32322  * resizer.east.setDisplayed(false);</p>
32323  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
32324  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
32325  * resize operation's new size (defaults to [0, 0])
32326  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
32327  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
32328  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
32329  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
32330  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
32331  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
32332  * @cfg {Number} width The width of the element in pixels (defaults to null)
32333  * @cfg {Number} height The height of the element in pixels (defaults to null)
32334  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
32335  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
32336  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
32337  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
32338  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
32339  * in favor of the handles config option (defaults to false)
32340  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
32341  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
32342  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
32343  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
32344  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
32345  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32346  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32347  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32348  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32349  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32350  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32351  * @constructor
32352  * Create a new resizable component
32353  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32354  * @param {Object} config configuration options
32355   */
32356 Roo.Resizable = function(el, config)
32357 {
32358     this.el = Roo.get(el);
32359
32360     if(config && config.wrap){
32361         config.resizeChild = this.el;
32362         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32363         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32364         this.el.setStyle("overflow", "hidden");
32365         this.el.setPositioning(config.resizeChild.getPositioning());
32366         config.resizeChild.clearPositioning();
32367         if(!config.width || !config.height){
32368             var csize = config.resizeChild.getSize();
32369             this.el.setSize(csize.width, csize.height);
32370         }
32371         if(config.pinned && !config.adjustments){
32372             config.adjustments = "auto";
32373         }
32374     }
32375
32376     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32377     this.proxy.unselectable();
32378     this.proxy.enableDisplayMode('block');
32379
32380     Roo.apply(this, config);
32381
32382     if(this.pinned){
32383         this.disableTrackOver = true;
32384         this.el.addClass("x-resizable-pinned");
32385     }
32386     // if the element isn't positioned, make it relative
32387     var position = this.el.getStyle("position");
32388     if(position != "absolute" && position != "fixed"){
32389         this.el.setStyle("position", "relative");
32390     }
32391     if(!this.handles){ // no handles passed, must be legacy style
32392         this.handles = 's,e,se';
32393         if(this.multiDirectional){
32394             this.handles += ',n,w';
32395         }
32396     }
32397     if(this.handles == "all"){
32398         this.handles = "n s e w ne nw se sw";
32399     }
32400     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32401     var ps = Roo.Resizable.positions;
32402     for(var i = 0, len = hs.length; i < len; i++){
32403         if(hs[i] && ps[hs[i]]){
32404             var pos = ps[hs[i]];
32405             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32406         }
32407     }
32408     // legacy
32409     this.corner = this.southeast;
32410     
32411     // updateBox = the box can move..
32412     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32413         this.updateBox = true;
32414     }
32415
32416     this.activeHandle = null;
32417
32418     if(this.resizeChild){
32419         if(typeof this.resizeChild == "boolean"){
32420             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32421         }else{
32422             this.resizeChild = Roo.get(this.resizeChild, true);
32423         }
32424     }
32425     
32426     if(this.adjustments == "auto"){
32427         var rc = this.resizeChild;
32428         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32429         if(rc && (hw || hn)){
32430             rc.position("relative");
32431             rc.setLeft(hw ? hw.el.getWidth() : 0);
32432             rc.setTop(hn ? hn.el.getHeight() : 0);
32433         }
32434         this.adjustments = [
32435             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32436             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32437         ];
32438     }
32439
32440     if(this.draggable){
32441         this.dd = this.dynamic ?
32442             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32443         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32444     }
32445
32446     // public events
32447     this.addEvents({
32448         /**
32449          * @event beforeresize
32450          * Fired before resize is allowed. Set enabled to false to cancel resize.
32451          * @param {Roo.Resizable} this
32452          * @param {Roo.EventObject} e The mousedown event
32453          */
32454         "beforeresize" : true,
32455         /**
32456          * @event resizing
32457          * Fired a resizing.
32458          * @param {Roo.Resizable} this
32459          * @param {Number} x The new x position
32460          * @param {Number} y The new y position
32461          * @param {Number} w The new w width
32462          * @param {Number} h The new h hight
32463          * @param {Roo.EventObject} e The mouseup event
32464          */
32465         "resizing" : true,
32466         /**
32467          * @event resize
32468          * Fired after a resize.
32469          * @param {Roo.Resizable} this
32470          * @param {Number} width The new width
32471          * @param {Number} height The new height
32472          * @param {Roo.EventObject} e The mouseup event
32473          */
32474         "resize" : true
32475     });
32476
32477     if(this.width !== null && this.height !== null){
32478         this.resizeTo(this.width, this.height);
32479     }else{
32480         this.updateChildSize();
32481     }
32482     if(Roo.isIE){
32483         this.el.dom.style.zoom = 1;
32484     }
32485     Roo.Resizable.superclass.constructor.call(this);
32486 };
32487
32488 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32489         resizeChild : false,
32490         adjustments : [0, 0],
32491         minWidth : 5,
32492         minHeight : 5,
32493         maxWidth : 10000,
32494         maxHeight : 10000,
32495         enabled : true,
32496         animate : false,
32497         duration : .35,
32498         dynamic : false,
32499         handles : false,
32500         multiDirectional : false,
32501         disableTrackOver : false,
32502         easing : 'easeOutStrong',
32503         widthIncrement : 0,
32504         heightIncrement : 0,
32505         pinned : false,
32506         width : null,
32507         height : null,
32508         preserveRatio : false,
32509         transparent: false,
32510         minX: 0,
32511         minY: 0,
32512         draggable: false,
32513
32514         /**
32515          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32516          */
32517         constrainTo: undefined,
32518         /**
32519          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32520          */
32521         resizeRegion: undefined,
32522
32523
32524     /**
32525      * Perform a manual resize
32526      * @param {Number} width
32527      * @param {Number} height
32528      */
32529     resizeTo : function(width, height){
32530         this.el.setSize(width, height);
32531         this.updateChildSize();
32532         this.fireEvent("resize", this, width, height, null);
32533     },
32534
32535     // private
32536     startSizing : function(e, handle){
32537         this.fireEvent("beforeresize", this, e);
32538         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32539
32540             if(!this.overlay){
32541                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32542                 this.overlay.unselectable();
32543                 this.overlay.enableDisplayMode("block");
32544                 this.overlay.on("mousemove", this.onMouseMove, this);
32545                 this.overlay.on("mouseup", this.onMouseUp, this);
32546             }
32547             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32548
32549             this.resizing = true;
32550             this.startBox = this.el.getBox();
32551             this.startPoint = e.getXY();
32552             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32553                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32554
32555             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32556             this.overlay.show();
32557
32558             if(this.constrainTo) {
32559                 var ct = Roo.get(this.constrainTo);
32560                 this.resizeRegion = ct.getRegion().adjust(
32561                     ct.getFrameWidth('t'),
32562                     ct.getFrameWidth('l'),
32563                     -ct.getFrameWidth('b'),
32564                     -ct.getFrameWidth('r')
32565                 );
32566             }
32567
32568             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32569             this.proxy.show();
32570             this.proxy.setBox(this.startBox);
32571             if(!this.dynamic){
32572                 this.proxy.setStyle('visibility', 'visible');
32573             }
32574         }
32575     },
32576
32577     // private
32578     onMouseDown : function(handle, e){
32579         if(this.enabled){
32580             e.stopEvent();
32581             this.activeHandle = handle;
32582             this.startSizing(e, handle);
32583         }
32584     },
32585
32586     // private
32587     onMouseUp : function(e){
32588         var size = this.resizeElement();
32589         this.resizing = false;
32590         this.handleOut();
32591         this.overlay.hide();
32592         this.proxy.hide();
32593         this.fireEvent("resize", this, size.width, size.height, e);
32594     },
32595
32596     // private
32597     updateChildSize : function(){
32598         
32599         if(this.resizeChild){
32600             var el = this.el;
32601             var child = this.resizeChild;
32602             var adj = this.adjustments;
32603             if(el.dom.offsetWidth){
32604                 var b = el.getSize(true);
32605                 child.setSize(b.width+adj[0], b.height+adj[1]);
32606             }
32607             // Second call here for IE
32608             // The first call enables instant resizing and
32609             // the second call corrects scroll bars if they
32610             // exist
32611             if(Roo.isIE){
32612                 setTimeout(function(){
32613                     if(el.dom.offsetWidth){
32614                         var b = el.getSize(true);
32615                         child.setSize(b.width+adj[0], b.height+adj[1]);
32616                     }
32617                 }, 10);
32618             }
32619         }
32620     },
32621
32622     // private
32623     snap : function(value, inc, min){
32624         if(!inc || !value) {
32625             return value;
32626         }
32627         var newValue = value;
32628         var m = value % inc;
32629         if(m > 0){
32630             if(m > (inc/2)){
32631                 newValue = value + (inc-m);
32632             }else{
32633                 newValue = value - m;
32634             }
32635         }
32636         return Math.max(min, newValue);
32637     },
32638
32639     // private
32640     resizeElement : function(){
32641         var box = this.proxy.getBox();
32642         if(this.updateBox){
32643             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32644         }else{
32645             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32646         }
32647         this.updateChildSize();
32648         if(!this.dynamic){
32649             this.proxy.hide();
32650         }
32651         return box;
32652     },
32653
32654     // private
32655     constrain : function(v, diff, m, mx){
32656         if(v - diff < m){
32657             diff = v - m;
32658         }else if(v - diff > mx){
32659             diff = mx - v;
32660         }
32661         return diff;
32662     },
32663
32664     // private
32665     onMouseMove : function(e){
32666         
32667         if(this.enabled){
32668             try{// try catch so if something goes wrong the user doesn't get hung
32669
32670             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32671                 return;
32672             }
32673
32674             //var curXY = this.startPoint;
32675             var curSize = this.curSize || this.startBox;
32676             var x = this.startBox.x, y = this.startBox.y;
32677             var ox = x, oy = y;
32678             var w = curSize.width, h = curSize.height;
32679             var ow = w, oh = h;
32680             var mw = this.minWidth, mh = this.minHeight;
32681             var mxw = this.maxWidth, mxh = this.maxHeight;
32682             var wi = this.widthIncrement;
32683             var hi = this.heightIncrement;
32684
32685             var eventXY = e.getXY();
32686             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32687             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32688
32689             var pos = this.activeHandle.position;
32690
32691             switch(pos){
32692                 case "east":
32693                     w += diffX;
32694                     w = Math.min(Math.max(mw, w), mxw);
32695                     break;
32696              
32697                 case "south":
32698                     h += diffY;
32699                     h = Math.min(Math.max(mh, h), mxh);
32700                     break;
32701                 case "southeast":
32702                     w += diffX;
32703                     h += diffY;
32704                     w = Math.min(Math.max(mw, w), mxw);
32705                     h = Math.min(Math.max(mh, h), mxh);
32706                     break;
32707                 case "north":
32708                     diffY = this.constrain(h, diffY, mh, mxh);
32709                     y += diffY;
32710                     h -= diffY;
32711                     break;
32712                 case "hdrag":
32713                     
32714                     if (wi) {
32715                         var adiffX = Math.abs(diffX);
32716                         var sub = (adiffX % wi); // how much 
32717                         if (sub > (wi/2)) { // far enough to snap
32718                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32719                         } else {
32720                             // remove difference.. 
32721                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32722                         }
32723                     }
32724                     x += diffX;
32725                     x = Math.max(this.minX, x);
32726                     break;
32727                 case "west":
32728                     diffX = this.constrain(w, diffX, mw, mxw);
32729                     x += diffX;
32730                     w -= diffX;
32731                     break;
32732                 case "northeast":
32733                     w += diffX;
32734                     w = Math.min(Math.max(mw, w), mxw);
32735                     diffY = this.constrain(h, diffY, mh, mxh);
32736                     y += diffY;
32737                     h -= diffY;
32738                     break;
32739                 case "northwest":
32740                     diffX = this.constrain(w, diffX, mw, mxw);
32741                     diffY = this.constrain(h, diffY, mh, mxh);
32742                     y += diffY;
32743                     h -= diffY;
32744                     x += diffX;
32745                     w -= diffX;
32746                     break;
32747                case "southwest":
32748                     diffX = this.constrain(w, diffX, mw, mxw);
32749                     h += diffY;
32750                     h = Math.min(Math.max(mh, h), mxh);
32751                     x += diffX;
32752                     w -= diffX;
32753                     break;
32754             }
32755
32756             var sw = this.snap(w, wi, mw);
32757             var sh = this.snap(h, hi, mh);
32758             if(sw != w || sh != h){
32759                 switch(pos){
32760                     case "northeast":
32761                         y -= sh - h;
32762                     break;
32763                     case "north":
32764                         y -= sh - h;
32765                         break;
32766                     case "southwest":
32767                         x -= sw - w;
32768                     break;
32769                     case "west":
32770                         x -= sw - w;
32771                         break;
32772                     case "northwest":
32773                         x -= sw - w;
32774                         y -= sh - h;
32775                     break;
32776                 }
32777                 w = sw;
32778                 h = sh;
32779             }
32780
32781             if(this.preserveRatio){
32782                 switch(pos){
32783                     case "southeast":
32784                     case "east":
32785                         h = oh * (w/ow);
32786                         h = Math.min(Math.max(mh, h), mxh);
32787                         w = ow * (h/oh);
32788                        break;
32789                     case "south":
32790                         w = ow * (h/oh);
32791                         w = Math.min(Math.max(mw, w), mxw);
32792                         h = oh * (w/ow);
32793                         break;
32794                     case "northeast":
32795                         w = ow * (h/oh);
32796                         w = Math.min(Math.max(mw, w), mxw);
32797                         h = oh * (w/ow);
32798                     break;
32799                     case "north":
32800                         var tw = w;
32801                         w = ow * (h/oh);
32802                         w = Math.min(Math.max(mw, w), mxw);
32803                         h = oh * (w/ow);
32804                         x += (tw - w) / 2;
32805                         break;
32806                     case "southwest":
32807                         h = oh * (w/ow);
32808                         h = Math.min(Math.max(mh, h), mxh);
32809                         var tw = w;
32810                         w = ow * (h/oh);
32811                         x += tw - w;
32812                         break;
32813                     case "west":
32814                         var th = h;
32815                         h = oh * (w/ow);
32816                         h = Math.min(Math.max(mh, h), mxh);
32817                         y += (th - h) / 2;
32818                         var tw = w;
32819                         w = ow * (h/oh);
32820                         x += tw - w;
32821                        break;
32822                     case "northwest":
32823                         var tw = w;
32824                         var th = h;
32825                         h = oh * (w/ow);
32826                         h = Math.min(Math.max(mh, h), mxh);
32827                         w = ow * (h/oh);
32828                         y += th - h;
32829                         x += tw - w;
32830                        break;
32831
32832                 }
32833             }
32834             if (pos == 'hdrag') {
32835                 w = ow;
32836             }
32837             this.proxy.setBounds(x, y, w, h);
32838             if(this.dynamic){
32839                 this.resizeElement();
32840             }
32841             }catch(e){}
32842         }
32843         this.fireEvent("resizing", this, x, y, w, h, e);
32844     },
32845
32846     // private
32847     handleOver : function(){
32848         if(this.enabled){
32849             this.el.addClass("x-resizable-over");
32850         }
32851     },
32852
32853     // private
32854     handleOut : function(){
32855         if(!this.resizing){
32856             this.el.removeClass("x-resizable-over");
32857         }
32858     },
32859
32860     /**
32861      * Returns the element this component is bound to.
32862      * @return {Roo.Element}
32863      */
32864     getEl : function(){
32865         return this.el;
32866     },
32867
32868     /**
32869      * Returns the resizeChild element (or null).
32870      * @return {Roo.Element}
32871      */
32872     getResizeChild : function(){
32873         return this.resizeChild;
32874     },
32875     groupHandler : function()
32876     {
32877         
32878     },
32879     /**
32880      * Destroys this resizable. If the element was wrapped and
32881      * removeEl is not true then the element remains.
32882      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32883      */
32884     destroy : function(removeEl){
32885         this.proxy.remove();
32886         if(this.overlay){
32887             this.overlay.removeAllListeners();
32888             this.overlay.remove();
32889         }
32890         var ps = Roo.Resizable.positions;
32891         for(var k in ps){
32892             if(typeof ps[k] != "function" && this[ps[k]]){
32893                 var h = this[ps[k]];
32894                 h.el.removeAllListeners();
32895                 h.el.remove();
32896             }
32897         }
32898         if(removeEl){
32899             this.el.update("");
32900             this.el.remove();
32901         }
32902     }
32903 });
32904
32905 // private
32906 // hash to map config positions to true positions
32907 Roo.Resizable.positions = {
32908     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32909     hd: "hdrag"
32910 };
32911
32912 // private
32913 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32914     if(!this.tpl){
32915         // only initialize the template if resizable is used
32916         var tpl = Roo.DomHelper.createTemplate(
32917             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32918         );
32919         tpl.compile();
32920         Roo.Resizable.Handle.prototype.tpl = tpl;
32921     }
32922     this.position = pos;
32923     this.rz = rz;
32924     // show north drag fro topdra
32925     var handlepos = pos == 'hdrag' ? 'north' : pos;
32926     
32927     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32928     if (pos == 'hdrag') {
32929         this.el.setStyle('cursor', 'pointer');
32930     }
32931     this.el.unselectable();
32932     if(transparent){
32933         this.el.setOpacity(0);
32934     }
32935     this.el.on("mousedown", this.onMouseDown, this);
32936     if(!disableTrackOver){
32937         this.el.on("mouseover", this.onMouseOver, this);
32938         this.el.on("mouseout", this.onMouseOut, this);
32939     }
32940 };
32941
32942 // private
32943 Roo.Resizable.Handle.prototype = {
32944     afterResize : function(rz){
32945         Roo.log('after?');
32946         // do nothing
32947     },
32948     // private
32949     onMouseDown : function(e){
32950         this.rz.onMouseDown(this, e);
32951     },
32952     // private
32953     onMouseOver : function(e){
32954         this.rz.handleOver(this, e);
32955     },
32956     // private
32957     onMouseOut : function(e){
32958         this.rz.handleOut(this, e);
32959     }
32960 };/*
32961  * Based on:
32962  * Ext JS Library 1.1.1
32963  * Copyright(c) 2006-2007, Ext JS, LLC.
32964  *
32965  * Originally Released Under LGPL - original licence link has changed is not relivant.
32966  *
32967  * Fork - LGPL
32968  * <script type="text/javascript">
32969  */
32970
32971 /**
32972  * @class Roo.Editor
32973  * @extends Roo.Component
32974  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32975  * @constructor
32976  * Create a new Editor
32977  * @param {Roo.form.Field} field The Field object (or descendant)
32978  * @param {Object} config The config object
32979  */
32980 Roo.Editor = function(field, config){
32981     Roo.Editor.superclass.constructor.call(this, config);
32982     this.field = field;
32983     this.addEvents({
32984         /**
32985              * @event beforestartedit
32986              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32987              * false from the handler of this event.
32988              * @param {Editor} this
32989              * @param {Roo.Element} boundEl The underlying element bound to this editor
32990              * @param {Mixed} value The field value being set
32991              */
32992         "beforestartedit" : true,
32993         /**
32994              * @event startedit
32995              * Fires when this editor is displayed
32996              * @param {Roo.Element} boundEl The underlying element bound to this editor
32997              * @param {Mixed} value The starting field value
32998              */
32999         "startedit" : true,
33000         /**
33001              * @event beforecomplete
33002              * Fires after a change has been made to the field, but before the change is reflected in the underlying
33003              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
33004              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
33005              * event will not fire since no edit actually occurred.
33006              * @param {Editor} this
33007              * @param {Mixed} value The current field value
33008              * @param {Mixed} startValue The original field value
33009              */
33010         "beforecomplete" : true,
33011         /**
33012              * @event complete
33013              * Fires after editing is complete and any changed value has been written to the underlying field.
33014              * @param {Editor} this
33015              * @param {Mixed} value The current field value
33016              * @param {Mixed} startValue The original field value
33017              */
33018         "complete" : true,
33019         /**
33020          * @event specialkey
33021          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
33022          * {@link Roo.EventObject#getKey} to determine which key was pressed.
33023          * @param {Roo.form.Field} this
33024          * @param {Roo.EventObject} e The event object
33025          */
33026         "specialkey" : true
33027     });
33028 };
33029
33030 Roo.extend(Roo.Editor, Roo.Component, {
33031     /**
33032      * @cfg {Boolean/String} autosize
33033      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
33034      * or "height" to adopt the height only (defaults to false)
33035      */
33036     /**
33037      * @cfg {Boolean} revertInvalid
33038      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
33039      * validation fails (defaults to true)
33040      */
33041     /**
33042      * @cfg {Boolean} ignoreNoChange
33043      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
33044      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
33045      * will never be ignored.
33046      */
33047     /**
33048      * @cfg {Boolean} hideEl
33049      * False to keep the bound element visible while the editor is displayed (defaults to true)
33050      */
33051     /**
33052      * @cfg {Mixed} value
33053      * The data value of the underlying field (defaults to "")
33054      */
33055     value : "",
33056     /**
33057      * @cfg {String} alignment
33058      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
33059      */
33060     alignment: "c-c?",
33061     /**
33062      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
33063      * for bottom-right shadow (defaults to "frame")
33064      */
33065     shadow : "frame",
33066     /**
33067      * @cfg {Boolean} constrain True to constrain the editor to the viewport
33068      */
33069     constrain : false,
33070     /**
33071      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
33072      */
33073     completeOnEnter : false,
33074     /**
33075      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
33076      */
33077     cancelOnEsc : false,
33078     /**
33079      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
33080      */
33081     updateEl : false,
33082
33083     // private
33084     onRender : function(ct, position){
33085         this.el = new Roo.Layer({
33086             shadow: this.shadow,
33087             cls: "x-editor",
33088             parentEl : ct,
33089             shim : this.shim,
33090             shadowOffset:4,
33091             id: this.id,
33092             constrain: this.constrain
33093         });
33094         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
33095         if(this.field.msgTarget != 'title'){
33096             this.field.msgTarget = 'qtip';
33097         }
33098         this.field.render(this.el);
33099         if(Roo.isGecko){
33100             this.field.el.dom.setAttribute('autocomplete', 'off');
33101         }
33102         this.field.on("specialkey", this.onSpecialKey, this);
33103         if(this.swallowKeys){
33104             this.field.el.swallowEvent(['keydown','keypress']);
33105         }
33106         this.field.show();
33107         this.field.on("blur", this.onBlur, this);
33108         if(this.field.grow){
33109             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
33110         }
33111     },
33112
33113     onSpecialKey : function(field, e)
33114     {
33115         //Roo.log('editor onSpecialKey');
33116         if(this.completeOnEnter && e.getKey() == e.ENTER){
33117             e.stopEvent();
33118             this.completeEdit();
33119             return;
33120         }
33121         // do not fire special key otherwise it might hide close the editor...
33122         if(e.getKey() == e.ENTER){    
33123             return;
33124         }
33125         if(this.cancelOnEsc && e.getKey() == e.ESC){
33126             this.cancelEdit();
33127             return;
33128         } 
33129         this.fireEvent('specialkey', field, e);
33130     
33131     },
33132
33133     /**
33134      * Starts the editing process and shows the editor.
33135      * @param {String/HTMLElement/Element} el The element to edit
33136      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
33137       * to the innerHTML of el.
33138      */
33139     startEdit : function(el, value){
33140         if(this.editing){
33141             this.completeEdit();
33142         }
33143         this.boundEl = Roo.get(el);
33144         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
33145         if(!this.rendered){
33146             this.render(this.parentEl || document.body);
33147         }
33148         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
33149             return;
33150         }
33151         this.startValue = v;
33152         this.field.setValue(v);
33153         if(this.autoSize){
33154             var sz = this.boundEl.getSize();
33155             switch(this.autoSize){
33156                 case "width":
33157                 this.setSize(sz.width,  "");
33158                 break;
33159                 case "height":
33160                 this.setSize("",  sz.height);
33161                 break;
33162                 default:
33163                 this.setSize(sz.width,  sz.height);
33164             }
33165         }
33166         this.el.alignTo(this.boundEl, this.alignment);
33167         this.editing = true;
33168         if(Roo.QuickTips){
33169             Roo.QuickTips.disable();
33170         }
33171         this.show();
33172     },
33173
33174     /**
33175      * Sets the height and width of this editor.
33176      * @param {Number} width The new width
33177      * @param {Number} height The new height
33178      */
33179     setSize : function(w, h){
33180         this.field.setSize(w, h);
33181         if(this.el){
33182             this.el.sync();
33183         }
33184     },
33185
33186     /**
33187      * Realigns the editor to the bound field based on the current alignment config value.
33188      */
33189     realign : function(){
33190         this.el.alignTo(this.boundEl, this.alignment);
33191     },
33192
33193     /**
33194      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
33195      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
33196      */
33197     completeEdit : function(remainVisible){
33198         if(!this.editing){
33199             return;
33200         }
33201         var v = this.getValue();
33202         if(this.revertInvalid !== false && !this.field.isValid()){
33203             v = this.startValue;
33204             this.cancelEdit(true);
33205         }
33206         if(String(v) === String(this.startValue) && this.ignoreNoChange){
33207             this.editing = false;
33208             this.hide();
33209             return;
33210         }
33211         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
33212             this.editing = false;
33213             if(this.updateEl && this.boundEl){
33214                 this.boundEl.update(v);
33215             }
33216             if(remainVisible !== true){
33217                 this.hide();
33218             }
33219             this.fireEvent("complete", this, v, this.startValue);
33220         }
33221     },
33222
33223     // private
33224     onShow : function(){
33225         this.el.show();
33226         if(this.hideEl !== false){
33227             this.boundEl.hide();
33228         }
33229         this.field.show();
33230         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
33231             this.fixIEFocus = true;
33232             this.deferredFocus.defer(50, this);
33233         }else{
33234             this.field.focus();
33235         }
33236         this.fireEvent("startedit", this.boundEl, this.startValue);
33237     },
33238
33239     deferredFocus : function(){
33240         if(this.editing){
33241             this.field.focus();
33242         }
33243     },
33244
33245     /**
33246      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
33247      * reverted to the original starting value.
33248      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
33249      * cancel (defaults to false)
33250      */
33251     cancelEdit : function(remainVisible){
33252         if(this.editing){
33253             this.setValue(this.startValue);
33254             if(remainVisible !== true){
33255                 this.hide();
33256             }
33257         }
33258     },
33259
33260     // private
33261     onBlur : function(){
33262         if(this.allowBlur !== true && this.editing){
33263             this.completeEdit();
33264         }
33265     },
33266
33267     // private
33268     onHide : function(){
33269         if(this.editing){
33270             this.completeEdit();
33271             return;
33272         }
33273         this.field.blur();
33274         if(this.field.collapse){
33275             this.field.collapse();
33276         }
33277         this.el.hide();
33278         if(this.hideEl !== false){
33279             this.boundEl.show();
33280         }
33281         if(Roo.QuickTips){
33282             Roo.QuickTips.enable();
33283         }
33284     },
33285
33286     /**
33287      * Sets the data value of the editor
33288      * @param {Mixed} value Any valid value supported by the underlying field
33289      */
33290     setValue : function(v){
33291         this.field.setValue(v);
33292     },
33293
33294     /**
33295      * Gets the data value of the editor
33296      * @return {Mixed} The data value
33297      */
33298     getValue : function(){
33299         return this.field.getValue();
33300     }
33301 });/*
33302  * Based on:
33303  * Ext JS Library 1.1.1
33304  * Copyright(c) 2006-2007, Ext JS, LLC.
33305  *
33306  * Originally Released Under LGPL - original licence link has changed is not relivant.
33307  *
33308  * Fork - LGPL
33309  * <script type="text/javascript">
33310  */
33311  
33312 /**
33313  * @class Roo.BasicDialog
33314  * @extends Roo.util.Observable
33315  * @parent none builder
33316  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
33317  * <pre><code>
33318 var dlg = new Roo.BasicDialog("my-dlg", {
33319     height: 200,
33320     width: 300,
33321     minHeight: 100,
33322     minWidth: 150,
33323     modal: true,
33324     proxyDrag: true,
33325     shadow: true
33326 });
33327 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
33328 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
33329 dlg.addButton('Cancel', dlg.hide, dlg);
33330 dlg.show();
33331 </code></pre>
33332   <b>A Dialog should always be a direct child of the body element.</b>
33333  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
33334  * @cfg {String} title Default text to display in the title bar (defaults to null)
33335  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33336  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
33337  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
33338  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
33339  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
33340  * (defaults to null with no animation)
33341  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
33342  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
33343  * property for valid values (defaults to 'all')
33344  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
33345  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
33346  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33347  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33348  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33349  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33350  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33351  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33352  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33353  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33354  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33355  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33356  * draggable = true (defaults to false)
33357  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33358  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33359  * shadow (defaults to false)
33360  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33361  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33362  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33363  * @cfg {Array} buttons Array of buttons
33364  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33365  * @constructor
33366  * Create a new BasicDialog.
33367  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33368  * @param {Object} config Configuration options
33369  */
33370 Roo.BasicDialog = function(el, config){
33371     this.el = Roo.get(el);
33372     var dh = Roo.DomHelper;
33373     if(!this.el && config && config.autoCreate){
33374         if(typeof config.autoCreate == "object"){
33375             if(!config.autoCreate.id){
33376                 config.autoCreate.id = el;
33377             }
33378             this.el = dh.append(document.body,
33379                         config.autoCreate, true);
33380         }else{
33381             this.el = dh.append(document.body,
33382                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33383         }
33384     }
33385     el = this.el;
33386     el.setDisplayed(true);
33387     el.hide = this.hideAction;
33388     this.id = el.id;
33389     el.addClass("x-dlg");
33390
33391     Roo.apply(this, config);
33392
33393     this.proxy = el.createProxy("x-dlg-proxy");
33394     this.proxy.hide = this.hideAction;
33395     this.proxy.setOpacity(.5);
33396     this.proxy.hide();
33397
33398     if(config.width){
33399         el.setWidth(config.width);
33400     }
33401     if(config.height){
33402         el.setHeight(config.height);
33403     }
33404     this.size = el.getSize();
33405     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33406         this.xy = [config.x,config.y];
33407     }else{
33408         this.xy = el.getCenterXY(true);
33409     }
33410     /** The header element @type Roo.Element */
33411     this.header = el.child("> .x-dlg-hd");
33412     /** The body element @type Roo.Element */
33413     this.body = el.child("> .x-dlg-bd");
33414     /** The footer element @type Roo.Element */
33415     this.footer = el.child("> .x-dlg-ft");
33416
33417     if(!this.header){
33418         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33419     }
33420     if(!this.body){
33421         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33422     }
33423
33424     this.header.unselectable();
33425     if(this.title){
33426         this.header.update(this.title);
33427     }
33428     // this element allows the dialog to be focused for keyboard event
33429     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33430     this.focusEl.swallowEvent("click", true);
33431
33432     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33433
33434     // wrap the body and footer for special rendering
33435     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33436     if(this.footer){
33437         this.bwrap.dom.appendChild(this.footer.dom);
33438     }
33439
33440     this.bg = this.el.createChild({
33441         tag: "div", cls:"x-dlg-bg",
33442         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33443     });
33444     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33445
33446
33447     if(this.autoScroll !== false && !this.autoTabs){
33448         this.body.setStyle("overflow", "auto");
33449     }
33450
33451     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33452
33453     if(this.closable !== false){
33454         this.el.addClass("x-dlg-closable");
33455         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33456         this.close.on("click", this.closeClick, this);
33457         this.close.addClassOnOver("x-dlg-close-over");
33458     }
33459     if(this.collapsible !== false){
33460         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33461         this.collapseBtn.on("click", this.collapseClick, this);
33462         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33463         this.header.on("dblclick", this.collapseClick, this);
33464     }
33465     if(this.resizable !== false){
33466         this.el.addClass("x-dlg-resizable");
33467         this.resizer = new Roo.Resizable(el, {
33468             minWidth: this.minWidth || 80,
33469             minHeight:this.minHeight || 80,
33470             handles: this.resizeHandles || "all",
33471             pinned: true
33472         });
33473         this.resizer.on("beforeresize", this.beforeResize, this);
33474         this.resizer.on("resize", this.onResize, this);
33475     }
33476     if(this.draggable !== false){
33477         el.addClass("x-dlg-draggable");
33478         if (!this.proxyDrag) {
33479             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33480         }
33481         else {
33482             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33483         }
33484         dd.setHandleElId(this.header.id);
33485         dd.endDrag = this.endMove.createDelegate(this);
33486         dd.startDrag = this.startMove.createDelegate(this);
33487         dd.onDrag = this.onDrag.createDelegate(this);
33488         dd.scroll = false;
33489         this.dd = dd;
33490     }
33491     if(this.modal){
33492         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33493         this.mask.enableDisplayMode("block");
33494         this.mask.hide();
33495         this.el.addClass("x-dlg-modal");
33496     }
33497     if(this.shadow){
33498         this.shadow = new Roo.Shadow({
33499             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33500             offset : this.shadowOffset
33501         });
33502     }else{
33503         this.shadowOffset = 0;
33504     }
33505     if(Roo.useShims && this.shim !== false){
33506         this.shim = this.el.createShim();
33507         this.shim.hide = this.hideAction;
33508         this.shim.hide();
33509     }else{
33510         this.shim = false;
33511     }
33512     if(this.autoTabs){
33513         this.initTabs();
33514     }
33515     if (this.buttons) { 
33516         var bts= this.buttons;
33517         this.buttons = [];
33518         Roo.each(bts, function(b) {
33519             this.addButton(b);
33520         }, this);
33521     }
33522     
33523     
33524     this.addEvents({
33525         /**
33526          * @event keydown
33527          * Fires when a key is pressed
33528          * @param {Roo.BasicDialog} this
33529          * @param {Roo.EventObject} e
33530          */
33531         "keydown" : true,
33532         /**
33533          * @event move
33534          * Fires when this dialog is moved by the user.
33535          * @param {Roo.BasicDialog} this
33536          * @param {Number} x The new page X
33537          * @param {Number} y The new page Y
33538          */
33539         "move" : true,
33540         /**
33541          * @event resize
33542          * Fires when this dialog is resized by the user.
33543          * @param {Roo.BasicDialog} this
33544          * @param {Number} width The new width
33545          * @param {Number} height The new height
33546          */
33547         "resize" : true,
33548         /**
33549          * @event beforehide
33550          * Fires before this dialog is hidden.
33551          * @param {Roo.BasicDialog} this
33552          */
33553         "beforehide" : true,
33554         /**
33555          * @event hide
33556          * Fires when this dialog is hidden.
33557          * @param {Roo.BasicDialog} this
33558          */
33559         "hide" : true,
33560         /**
33561          * @event beforeshow
33562          * Fires before this dialog is shown.
33563          * @param {Roo.BasicDialog} this
33564          */
33565         "beforeshow" : true,
33566         /**
33567          * @event show
33568          * Fires when this dialog is shown.
33569          * @param {Roo.BasicDialog} this
33570          */
33571         "show" : true
33572     });
33573     el.on("keydown", this.onKeyDown, this);
33574     el.on("mousedown", this.toFront, this);
33575     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33576     this.el.hide();
33577     Roo.DialogManager.register(this);
33578     Roo.BasicDialog.superclass.constructor.call(this);
33579 };
33580
33581 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33582     shadowOffset: Roo.isIE ? 6 : 5,
33583     minHeight: 80,
33584     minWidth: 200,
33585     minButtonWidth: 75,
33586     defaultButton: null,
33587     buttonAlign: "right",
33588     tabTag: 'div',
33589     firstShow: true,
33590
33591     /**
33592      * Sets the dialog title text
33593      * @param {String} text The title text to display
33594      * @return {Roo.BasicDialog} this
33595      */
33596     setTitle : function(text){
33597         this.header.update(text);
33598         return this;
33599     },
33600
33601     // private
33602     closeClick : function(){
33603         this.hide();
33604     },
33605
33606     // private
33607     collapseClick : function(){
33608         this[this.collapsed ? "expand" : "collapse"]();
33609     },
33610
33611     /**
33612      * Collapses the dialog to its minimized state (only the title bar is visible).
33613      * Equivalent to the user clicking the collapse dialog button.
33614      */
33615     collapse : function(){
33616         if(!this.collapsed){
33617             this.collapsed = true;
33618             this.el.addClass("x-dlg-collapsed");
33619             this.restoreHeight = this.el.getHeight();
33620             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33621         }
33622     },
33623
33624     /**
33625      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33626      * clicking the expand dialog button.
33627      */
33628     expand : function(){
33629         if(this.collapsed){
33630             this.collapsed = false;
33631             this.el.removeClass("x-dlg-collapsed");
33632             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33633         }
33634     },
33635
33636     /**
33637      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33638      * @return {Roo.TabPanel} The tabs component
33639      */
33640     initTabs : function(){
33641         var tabs = this.getTabs();
33642         while(tabs.getTab(0)){
33643             tabs.removeTab(0);
33644         }
33645         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33646             var dom = el.dom;
33647             tabs.addTab(Roo.id(dom), dom.title);
33648             dom.title = "";
33649         });
33650         tabs.activate(0);
33651         return tabs;
33652     },
33653
33654     // private
33655     beforeResize : function(){
33656         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33657     },
33658
33659     // private
33660     onResize : function(){
33661         this.refreshSize();
33662         this.syncBodyHeight();
33663         this.adjustAssets();
33664         this.focus();
33665         this.fireEvent("resize", this, this.size.width, this.size.height);
33666     },
33667
33668     // private
33669     onKeyDown : function(e){
33670         if(this.isVisible()){
33671             this.fireEvent("keydown", this, e);
33672         }
33673     },
33674
33675     /**
33676      * Resizes the dialog.
33677      * @param {Number} width
33678      * @param {Number} height
33679      * @return {Roo.BasicDialog} this
33680      */
33681     resizeTo : function(width, height){
33682         this.el.setSize(width, height);
33683         this.size = {width: width, height: height};
33684         this.syncBodyHeight();
33685         if(this.fixedcenter){
33686             this.center();
33687         }
33688         if(this.isVisible()){
33689             this.constrainXY();
33690             this.adjustAssets();
33691         }
33692         this.fireEvent("resize", this, width, height);
33693         return this;
33694     },
33695
33696
33697     /**
33698      * Resizes the dialog to fit the specified content size.
33699      * @param {Number} width
33700      * @param {Number} height
33701      * @return {Roo.BasicDialog} this
33702      */
33703     setContentSize : function(w, h){
33704         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33705         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33706         //if(!this.el.isBorderBox()){
33707             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33708             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33709         //}
33710         if(this.tabs){
33711             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33712             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33713         }
33714         this.resizeTo(w, h);
33715         return this;
33716     },
33717
33718     /**
33719      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33720      * executed in response to a particular key being pressed while the dialog is active.
33721      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33722      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33723      * @param {Function} fn The function to call
33724      * @param {Object} scope (optional) The scope of the function
33725      * @return {Roo.BasicDialog} this
33726      */
33727     addKeyListener : function(key, fn, scope){
33728         var keyCode, shift, ctrl, alt;
33729         if(typeof key == "object" && !(key instanceof Array)){
33730             keyCode = key["key"];
33731             shift = key["shift"];
33732             ctrl = key["ctrl"];
33733             alt = key["alt"];
33734         }else{
33735             keyCode = key;
33736         }
33737         var handler = function(dlg, e){
33738             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33739                 var k = e.getKey();
33740                 if(keyCode instanceof Array){
33741                     for(var i = 0, len = keyCode.length; i < len; i++){
33742                         if(keyCode[i] == k){
33743                           fn.call(scope || window, dlg, k, e);
33744                           return;
33745                         }
33746                     }
33747                 }else{
33748                     if(k == keyCode){
33749                         fn.call(scope || window, dlg, k, e);
33750                     }
33751                 }
33752             }
33753         };
33754         this.on("keydown", handler);
33755         return this;
33756     },
33757
33758     /**
33759      * Returns the TabPanel component (creates it if it doesn't exist).
33760      * Note: If you wish to simply check for the existence of tabs without creating them,
33761      * check for a null 'tabs' property.
33762      * @return {Roo.TabPanel} The tabs component
33763      */
33764     getTabs : function(){
33765         if(!this.tabs){
33766             this.el.addClass("x-dlg-auto-tabs");
33767             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33768             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33769         }
33770         return this.tabs;
33771     },
33772
33773     /**
33774      * Adds a button to the footer section of the dialog.
33775      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33776      * object or a valid Roo.DomHelper element config
33777      * @param {Function} handler The function called when the button is clicked
33778      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33779      * @return {Roo.Button} The new button
33780      */
33781     addButton : function(config, handler, scope){
33782         var dh = Roo.DomHelper;
33783         if(!this.footer){
33784             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33785         }
33786         if(!this.btnContainer){
33787             var tb = this.footer.createChild({
33788
33789                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33790                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33791             }, null, true);
33792             this.btnContainer = tb.firstChild.firstChild.firstChild;
33793         }
33794         var bconfig = {
33795             handler: handler,
33796             scope: scope,
33797             minWidth: this.minButtonWidth,
33798             hideParent:true
33799         };
33800         if(typeof config == "string"){
33801             bconfig.text = config;
33802         }else{
33803             if(config.tag){
33804                 bconfig.dhconfig = config;
33805             }else{
33806                 Roo.apply(bconfig, config);
33807             }
33808         }
33809         var fc = false;
33810         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33811             bconfig.position = Math.max(0, bconfig.position);
33812             fc = this.btnContainer.childNodes[bconfig.position];
33813         }
33814          
33815         var btn = new Roo.Button(
33816             fc ? 
33817                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33818                 : this.btnContainer.appendChild(document.createElement("td")),
33819             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33820             bconfig
33821         );
33822         this.syncBodyHeight();
33823         if(!this.buttons){
33824             /**
33825              * Array of all the buttons that have been added to this dialog via addButton
33826              * @type Array
33827              */
33828             this.buttons = [];
33829         }
33830         this.buttons.push(btn);
33831         return btn;
33832     },
33833
33834     /**
33835      * Sets the default button to be focused when the dialog is displayed.
33836      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33837      * @return {Roo.BasicDialog} this
33838      */
33839     setDefaultButton : function(btn){
33840         this.defaultButton = btn;
33841         return this;
33842     },
33843
33844     // private
33845     getHeaderFooterHeight : function(safe){
33846         var height = 0;
33847         if(this.header){
33848            height += this.header.getHeight();
33849         }
33850         if(this.footer){
33851            var fm = this.footer.getMargins();
33852             height += (this.footer.getHeight()+fm.top+fm.bottom);
33853         }
33854         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33855         height += this.centerBg.getPadding("tb");
33856         return height;
33857     },
33858
33859     // private
33860     syncBodyHeight : function()
33861     {
33862         var bd = this.body, // the text
33863             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33864             bw = this.bwrap;
33865         var height = this.size.height - this.getHeaderFooterHeight(false);
33866         bd.setHeight(height-bd.getMargins("tb"));
33867         var hh = this.header.getHeight();
33868         var h = this.size.height-hh;
33869         cb.setHeight(h);
33870         
33871         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33872         bw.setHeight(h-cb.getPadding("tb"));
33873         
33874         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33875         bd.setWidth(bw.getWidth(true));
33876         if(this.tabs){
33877             this.tabs.syncHeight();
33878             if(Roo.isIE){
33879                 this.tabs.el.repaint();
33880             }
33881         }
33882     },
33883
33884     /**
33885      * Restores the previous state of the dialog if Roo.state is configured.
33886      * @return {Roo.BasicDialog} this
33887      */
33888     restoreState : function(){
33889         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33890         if(box && box.width){
33891             this.xy = [box.x, box.y];
33892             this.resizeTo(box.width, box.height);
33893         }
33894         return this;
33895     },
33896
33897     // private
33898     beforeShow : function(){
33899         this.expand();
33900         if(this.fixedcenter){
33901             this.xy = this.el.getCenterXY(true);
33902         }
33903         if(this.modal){
33904             Roo.get(document.body).addClass("x-body-masked");
33905             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33906             this.mask.show();
33907         }
33908         this.constrainXY();
33909     },
33910
33911     // private
33912     animShow : function(){
33913         var b = Roo.get(this.animateTarget).getBox();
33914         this.proxy.setSize(b.width, b.height);
33915         this.proxy.setLocation(b.x, b.y);
33916         this.proxy.show();
33917         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33918                     true, .35, this.showEl.createDelegate(this));
33919     },
33920
33921     /**
33922      * Shows the dialog.
33923      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33924      * @return {Roo.BasicDialog} this
33925      */
33926     show : function(animateTarget){
33927         if (this.fireEvent("beforeshow", this) === false){
33928             return;
33929         }
33930         if(this.syncHeightBeforeShow){
33931             this.syncBodyHeight();
33932         }else if(this.firstShow){
33933             this.firstShow = false;
33934             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33935         }
33936         this.animateTarget = animateTarget || this.animateTarget;
33937         if(!this.el.isVisible()){
33938             this.beforeShow();
33939             if(this.animateTarget && Roo.get(this.animateTarget)){
33940                 this.animShow();
33941             }else{
33942                 this.showEl();
33943             }
33944         }
33945         return this;
33946     },
33947
33948     // private
33949     showEl : function(){
33950         this.proxy.hide();
33951         this.el.setXY(this.xy);
33952         this.el.show();
33953         this.adjustAssets(true);
33954         this.toFront();
33955         this.focus();
33956         // IE peekaboo bug - fix found by Dave Fenwick
33957         if(Roo.isIE){
33958             this.el.repaint();
33959         }
33960         this.fireEvent("show", this);
33961     },
33962
33963     /**
33964      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33965      * dialog itself will receive focus.
33966      */
33967     focus : function(){
33968         if(this.defaultButton){
33969             this.defaultButton.focus();
33970         }else{
33971             this.focusEl.focus();
33972         }
33973     },
33974
33975     // private
33976     constrainXY : function(){
33977         if(this.constraintoviewport !== false){
33978             if(!this.viewSize){
33979                 if(this.container){
33980                     var s = this.container.getSize();
33981                     this.viewSize = [s.width, s.height];
33982                 }else{
33983                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33984                 }
33985             }
33986             var s = Roo.get(this.container||document).getScroll();
33987
33988             var x = this.xy[0], y = this.xy[1];
33989             var w = this.size.width, h = this.size.height;
33990             var vw = this.viewSize[0], vh = this.viewSize[1];
33991             // only move it if it needs it
33992             var moved = false;
33993             // first validate right/bottom
33994             if(x + w > vw+s.left){
33995                 x = vw - w;
33996                 moved = true;
33997             }
33998             if(y + h > vh+s.top){
33999                 y = vh - h;
34000                 moved = true;
34001             }
34002             // then make sure top/left isn't negative
34003             if(x < s.left){
34004                 x = s.left;
34005                 moved = true;
34006             }
34007             if(y < s.top){
34008                 y = s.top;
34009                 moved = true;
34010             }
34011             if(moved){
34012                 // cache xy
34013                 this.xy = [x, y];
34014                 if(this.isVisible()){
34015                     this.el.setLocation(x, y);
34016                     this.adjustAssets();
34017                 }
34018             }
34019         }
34020     },
34021
34022     // private
34023     onDrag : function(){
34024         if(!this.proxyDrag){
34025             this.xy = this.el.getXY();
34026             this.adjustAssets();
34027         }
34028     },
34029
34030     // private
34031     adjustAssets : function(doShow){
34032         var x = this.xy[0], y = this.xy[1];
34033         var w = this.size.width, h = this.size.height;
34034         if(doShow === true){
34035             if(this.shadow){
34036                 this.shadow.show(this.el);
34037             }
34038             if(this.shim){
34039                 this.shim.show();
34040             }
34041         }
34042         if(this.shadow && this.shadow.isVisible()){
34043             this.shadow.show(this.el);
34044         }
34045         if(this.shim && this.shim.isVisible()){
34046             this.shim.setBounds(x, y, w, h);
34047         }
34048     },
34049
34050     // private
34051     adjustViewport : function(w, h){
34052         if(!w || !h){
34053             w = Roo.lib.Dom.getViewWidth();
34054             h = Roo.lib.Dom.getViewHeight();
34055         }
34056         // cache the size
34057         this.viewSize = [w, h];
34058         if(this.modal && this.mask.isVisible()){
34059             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
34060             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
34061         }
34062         if(this.isVisible()){
34063             this.constrainXY();
34064         }
34065     },
34066
34067     /**
34068      * Destroys this dialog and all its supporting elements (including any tabs, shim,
34069      * shadow, proxy, mask, etc.)  Also removes all event listeners.
34070      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
34071      */
34072     destroy : function(removeEl){
34073         if(this.isVisible()){
34074             this.animateTarget = null;
34075             this.hide();
34076         }
34077         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
34078         if(this.tabs){
34079             this.tabs.destroy(removeEl);
34080         }
34081         Roo.destroy(
34082              this.shim,
34083              this.proxy,
34084              this.resizer,
34085              this.close,
34086              this.mask
34087         );
34088         if(this.dd){
34089             this.dd.unreg();
34090         }
34091         if(this.buttons){
34092            for(var i = 0, len = this.buttons.length; i < len; i++){
34093                this.buttons[i].destroy();
34094            }
34095         }
34096         this.el.removeAllListeners();
34097         if(removeEl === true){
34098             this.el.update("");
34099             this.el.remove();
34100         }
34101         Roo.DialogManager.unregister(this);
34102     },
34103
34104     // private
34105     startMove : function(){
34106         if(this.proxyDrag){
34107             this.proxy.show();
34108         }
34109         if(this.constraintoviewport !== false){
34110             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
34111         }
34112     },
34113
34114     // private
34115     endMove : function(){
34116         if(!this.proxyDrag){
34117             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
34118         }else{
34119             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
34120             this.proxy.hide();
34121         }
34122         this.refreshSize();
34123         this.adjustAssets();
34124         this.focus();
34125         this.fireEvent("move", this, this.xy[0], this.xy[1]);
34126     },
34127
34128     /**
34129      * Brings this dialog to the front of any other visible dialogs
34130      * @return {Roo.BasicDialog} this
34131      */
34132     toFront : function(){
34133         Roo.DialogManager.bringToFront(this);
34134         return this;
34135     },
34136
34137     /**
34138      * Sends this dialog to the back (under) of any other visible dialogs
34139      * @return {Roo.BasicDialog} this
34140      */
34141     toBack : function(){
34142         Roo.DialogManager.sendToBack(this);
34143         return this;
34144     },
34145
34146     /**
34147      * Centers this dialog in the viewport
34148      * @return {Roo.BasicDialog} this
34149      */
34150     center : function(){
34151         var xy = this.el.getCenterXY(true);
34152         this.moveTo(xy[0], xy[1]);
34153         return this;
34154     },
34155
34156     /**
34157      * Moves the dialog's top-left corner to the specified point
34158      * @param {Number} x
34159      * @param {Number} y
34160      * @return {Roo.BasicDialog} this
34161      */
34162     moveTo : function(x, y){
34163         this.xy = [x,y];
34164         if(this.isVisible()){
34165             this.el.setXY(this.xy);
34166             this.adjustAssets();
34167         }
34168         return this;
34169     },
34170
34171     /**
34172      * Aligns the dialog to the specified element
34173      * @param {String/HTMLElement/Roo.Element} element The element to align to.
34174      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
34175      * @param {Array} offsets (optional) Offset the positioning by [x, y]
34176      * @return {Roo.BasicDialog} this
34177      */
34178     alignTo : function(element, position, offsets){
34179         this.xy = this.el.getAlignToXY(element, position, offsets);
34180         if(this.isVisible()){
34181             this.el.setXY(this.xy);
34182             this.adjustAssets();
34183         }
34184         return this;
34185     },
34186
34187     /**
34188      * Anchors an element to another element and realigns it when the window is resized.
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      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
34193      * is a number, it is used as the buffer delay (defaults to 50ms).
34194      * @return {Roo.BasicDialog} this
34195      */
34196     anchorTo : function(el, alignment, offsets, monitorScroll){
34197         var action = function(){
34198             this.alignTo(el, alignment, offsets);
34199         };
34200         Roo.EventManager.onWindowResize(action, this);
34201         var tm = typeof monitorScroll;
34202         if(tm != 'undefined'){
34203             Roo.EventManager.on(window, 'scroll', action, this,
34204                 {buffer: tm == 'number' ? monitorScroll : 50});
34205         }
34206         action.call(this);
34207         return this;
34208     },
34209
34210     /**
34211      * Returns true if the dialog is visible
34212      * @return {Boolean}
34213      */
34214     isVisible : function(){
34215         return this.el.isVisible();
34216     },
34217
34218     // private
34219     animHide : function(callback){
34220         var b = Roo.get(this.animateTarget).getBox();
34221         this.proxy.show();
34222         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
34223         this.el.hide();
34224         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
34225                     this.hideEl.createDelegate(this, [callback]));
34226     },
34227
34228     /**
34229      * Hides the dialog.
34230      * @param {Function} callback (optional) Function to call when the dialog is hidden
34231      * @return {Roo.BasicDialog} this
34232      */
34233     hide : function(callback){
34234         if (this.fireEvent("beforehide", this) === false){
34235             return;
34236         }
34237         if(this.shadow){
34238             this.shadow.hide();
34239         }
34240         if(this.shim) {
34241           this.shim.hide();
34242         }
34243         // sometimes animateTarget seems to get set.. causing problems...
34244         // this just double checks..
34245         if(this.animateTarget && Roo.get(this.animateTarget)) {
34246            this.animHide(callback);
34247         }else{
34248             this.el.hide();
34249             this.hideEl(callback);
34250         }
34251         return this;
34252     },
34253
34254     // private
34255     hideEl : function(callback){
34256         this.proxy.hide();
34257         if(this.modal){
34258             this.mask.hide();
34259             Roo.get(document.body).removeClass("x-body-masked");
34260         }
34261         this.fireEvent("hide", this);
34262         if(typeof callback == "function"){
34263             callback();
34264         }
34265     },
34266
34267     // private
34268     hideAction : function(){
34269         this.setLeft("-10000px");
34270         this.setTop("-10000px");
34271         this.setStyle("visibility", "hidden");
34272     },
34273
34274     // private
34275     refreshSize : function(){
34276         this.size = this.el.getSize();
34277         this.xy = this.el.getXY();
34278         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
34279     },
34280
34281     // private
34282     // z-index is managed by the DialogManager and may be overwritten at any time
34283     setZIndex : function(index){
34284         if(this.modal){
34285             this.mask.setStyle("z-index", index);
34286         }
34287         if(this.shim){
34288             this.shim.setStyle("z-index", ++index);
34289         }
34290         if(this.shadow){
34291             this.shadow.setZIndex(++index);
34292         }
34293         this.el.setStyle("z-index", ++index);
34294         if(this.proxy){
34295             this.proxy.setStyle("z-index", ++index);
34296         }
34297         if(this.resizer){
34298             this.resizer.proxy.setStyle("z-index", ++index);
34299         }
34300
34301         this.lastZIndex = index;
34302     },
34303
34304     /**
34305      * Returns the element for this dialog
34306      * @return {Roo.Element} The underlying dialog Element
34307      */
34308     getEl : function(){
34309         return this.el;
34310     }
34311 });
34312
34313 /**
34314  * @class Roo.DialogManager
34315  * Provides global access to BasicDialogs that have been created and
34316  * support for z-indexing (layering) multiple open dialogs.
34317  */
34318 Roo.DialogManager = function(){
34319     var list = {};
34320     var accessList = [];
34321     var front = null;
34322
34323     // private
34324     var sortDialogs = function(d1, d2){
34325         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
34326     };
34327
34328     // private
34329     var orderDialogs = function(){
34330         accessList.sort(sortDialogs);
34331         var seed = Roo.DialogManager.zseed;
34332         for(var i = 0, len = accessList.length; i < len; i++){
34333             var dlg = accessList[i];
34334             if(dlg){
34335                 dlg.setZIndex(seed + (i*10));
34336             }
34337         }
34338     };
34339
34340     return {
34341         /**
34342          * The starting z-index for BasicDialogs (defaults to 9000)
34343          * @type Number The z-index value
34344          */
34345         zseed : 9000,
34346
34347         // private
34348         register : function(dlg){
34349             list[dlg.id] = dlg;
34350             accessList.push(dlg);
34351         },
34352
34353         // private
34354         unregister : function(dlg){
34355             delete list[dlg.id];
34356             var i=0;
34357             var len=0;
34358             if(!accessList.indexOf){
34359                 for(  i = 0, len = accessList.length; i < len; i++){
34360                     if(accessList[i] == dlg){
34361                         accessList.splice(i, 1);
34362                         return;
34363                     }
34364                 }
34365             }else{
34366                  i = accessList.indexOf(dlg);
34367                 if(i != -1){
34368                     accessList.splice(i, 1);
34369                 }
34370             }
34371         },
34372
34373         /**
34374          * Gets a registered dialog by id
34375          * @param {String/Object} id The id of the dialog or a dialog
34376          * @return {Roo.BasicDialog} this
34377          */
34378         get : function(id){
34379             return typeof id == "object" ? id : list[id];
34380         },
34381
34382         /**
34383          * Brings the specified dialog to the front
34384          * @param {String/Object} dlg The id of the dialog or a dialog
34385          * @return {Roo.BasicDialog} this
34386          */
34387         bringToFront : function(dlg){
34388             dlg = this.get(dlg);
34389             if(dlg != front){
34390                 front = dlg;
34391                 dlg._lastAccess = new Date().getTime();
34392                 orderDialogs();
34393             }
34394             return dlg;
34395         },
34396
34397         /**
34398          * Sends the specified dialog to the back
34399          * @param {String/Object} dlg The id of the dialog or a dialog
34400          * @return {Roo.BasicDialog} this
34401          */
34402         sendToBack : function(dlg){
34403             dlg = this.get(dlg);
34404             dlg._lastAccess = -(new Date().getTime());
34405             orderDialogs();
34406             return dlg;
34407         },
34408
34409         /**
34410          * Hides all dialogs
34411          */
34412         hideAll : function(){
34413             for(var id in list){
34414                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34415                     list[id].hide();
34416                 }
34417             }
34418         }
34419     };
34420 }();
34421
34422 /**
34423  * @class Roo.LayoutDialog
34424  * @extends Roo.BasicDialog
34425  * @children Roo.ContentPanel
34426  * @parent builder none
34427  * Dialog which provides adjustments for working with a layout in a Dialog.
34428  * Add your necessary layout config options to the dialog's config.<br>
34429  * Example usage (including a nested layout):
34430  * <pre><code>
34431 if(!dialog){
34432     dialog = new Roo.LayoutDialog("download-dlg", {
34433         modal: true,
34434         width:600,
34435         height:450,
34436         shadow:true,
34437         minWidth:500,
34438         minHeight:350,
34439         autoTabs:true,
34440         proxyDrag:true,
34441         // layout config merges with the dialog config
34442         center:{
34443             tabPosition: "top",
34444             alwaysShowTabs: true
34445         }
34446     });
34447     dialog.addKeyListener(27, dialog.hide, dialog);
34448     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34449     dialog.addButton("Build It!", this.getDownload, this);
34450
34451     // we can even add nested layouts
34452     var innerLayout = new Roo.BorderLayout("dl-inner", {
34453         east: {
34454             initialSize: 200,
34455             autoScroll:true,
34456             split:true
34457         },
34458         center: {
34459             autoScroll:true
34460         }
34461     });
34462     innerLayout.beginUpdate();
34463     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34464     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34465     innerLayout.endUpdate(true);
34466
34467     var layout = dialog.getLayout();
34468     layout.beginUpdate();
34469     layout.add("center", new Roo.ContentPanel("standard-panel",
34470                         {title: "Download the Source", fitToFrame:true}));
34471     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34472                {title: "Build your own roo.js"}));
34473     layout.getRegion("center").showPanel(sp);
34474     layout.endUpdate();
34475 }
34476 </code></pre>
34477     * @constructor
34478     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34479     * @param {Object} config configuration options
34480   */
34481 Roo.LayoutDialog = function(el, cfg){
34482     
34483     var config=  cfg;
34484     if (typeof(cfg) == 'undefined') {
34485         config = Roo.apply({}, el);
34486         // not sure why we use documentElement here.. - it should always be body.
34487         // IE7 borks horribly if we use documentElement.
34488         // webkit also does not like documentElement - it creates a body element...
34489         el = Roo.get( document.body || document.documentElement ).createChild();
34490         //config.autoCreate = true;
34491     }
34492     
34493     
34494     config.autoTabs = false;
34495     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34496     this.body.setStyle({overflow:"hidden", position:"relative"});
34497     this.layout = new Roo.BorderLayout(this.body.dom, config);
34498     this.layout.monitorWindowResize = false;
34499     this.el.addClass("x-dlg-auto-layout");
34500     // fix case when center region overwrites center function
34501     this.center = Roo.BasicDialog.prototype.center;
34502     this.on("show", this.layout.layout, this.layout, true);
34503     if (config.items) {
34504         var xitems = config.items;
34505         delete config.items;
34506         Roo.each(xitems, this.addxtype, this);
34507     }
34508     
34509     
34510 };
34511 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34512     
34513     
34514     /**
34515      * @cfg {Roo.LayoutRegion} east  
34516      */
34517     /**
34518      * @cfg {Roo.LayoutRegion} west
34519      */
34520     /**
34521      * @cfg {Roo.LayoutRegion} south
34522      */
34523     /**
34524      * @cfg {Roo.LayoutRegion} north
34525      */
34526     /**
34527      * @cfg {Roo.LayoutRegion} center
34528      */
34529     /**
34530      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34531      */
34532     
34533     
34534     /**
34535      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34536      * @deprecated
34537      */
34538     endUpdate : function(){
34539         this.layout.endUpdate();
34540     },
34541
34542     /**
34543      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34544      *  @deprecated
34545      */
34546     beginUpdate : function(){
34547         this.layout.beginUpdate();
34548     },
34549
34550     /**
34551      * Get the BorderLayout for this dialog
34552      * @return {Roo.BorderLayout}
34553      */
34554     getLayout : function(){
34555         return this.layout;
34556     },
34557
34558     showEl : function(){
34559         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34560         if(Roo.isIE7){
34561             this.layout.layout();
34562         }
34563     },
34564
34565     // private
34566     // Use the syncHeightBeforeShow config option to control this automatically
34567     syncBodyHeight : function(){
34568         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34569         if(this.layout){this.layout.layout();}
34570     },
34571     
34572       /**
34573      * Add an xtype element (actually adds to the layout.)
34574      * @return {Object} xdata xtype object data.
34575      */
34576     
34577     addxtype : function(c) {
34578         return this.layout.addxtype(c);
34579     }
34580 });/*
34581  * Based on:
34582  * Ext JS Library 1.1.1
34583  * Copyright(c) 2006-2007, Ext JS, LLC.
34584  *
34585  * Originally Released Under LGPL - original licence link has changed is not relivant.
34586  *
34587  * Fork - LGPL
34588  * <script type="text/javascript">
34589  */
34590  
34591 /**
34592  * @class Roo.MessageBox
34593  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34594  * Example usage:
34595  *<pre><code>
34596 // Basic alert:
34597 Roo.Msg.alert('Status', 'Changes saved successfully.');
34598
34599 // Prompt for user data:
34600 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34601     if (btn == 'ok'){
34602         // process text value...
34603     }
34604 });
34605
34606 // Show a dialog using config options:
34607 Roo.Msg.show({
34608    title:'Save Changes?',
34609    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34610    buttons: Roo.Msg.YESNOCANCEL,
34611    fn: processResult,
34612    animEl: 'elId'
34613 });
34614 </code></pre>
34615  * @static
34616  */
34617 Roo.MessageBox = function(){
34618     var dlg, opt, mask, waitTimer;
34619     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34620     var buttons, activeTextEl, bwidth;
34621
34622     // private
34623     var handleButton = function(button){
34624         dlg.hide();
34625         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34626     };
34627
34628     // private
34629     var handleHide = function(){
34630         if(opt && opt.cls){
34631             dlg.el.removeClass(opt.cls);
34632         }
34633         if(waitTimer){
34634             Roo.TaskMgr.stop(waitTimer);
34635             waitTimer = null;
34636         }
34637     };
34638
34639     // private
34640     var updateButtons = function(b){
34641         var width = 0;
34642         if(!b){
34643             buttons["ok"].hide();
34644             buttons["cancel"].hide();
34645             buttons["yes"].hide();
34646             buttons["no"].hide();
34647             dlg.footer.dom.style.display = 'none';
34648             return width;
34649         }
34650         dlg.footer.dom.style.display = '';
34651         for(var k in buttons){
34652             if(typeof buttons[k] != "function"){
34653                 if(b[k]){
34654                     buttons[k].show();
34655                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34656                     width += buttons[k].el.getWidth()+15;
34657                 }else{
34658                     buttons[k].hide();
34659                 }
34660             }
34661         }
34662         return width;
34663     };
34664
34665     // private
34666     var handleEsc = function(d, k, e){
34667         if(opt && opt.closable !== false){
34668             dlg.hide();
34669         }
34670         if(e){
34671             e.stopEvent();
34672         }
34673     };
34674
34675     return {
34676         /**
34677          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34678          * @return {Roo.BasicDialog} The BasicDialog element
34679          */
34680         getDialog : function(){
34681            if(!dlg){
34682                 dlg = new Roo.BasicDialog("x-msg-box", {
34683                     autoCreate : true,
34684                     shadow: true,
34685                     draggable: true,
34686                     resizable:false,
34687                     constraintoviewport:false,
34688                     fixedcenter:true,
34689                     collapsible : false,
34690                     shim:true,
34691                     modal: true,
34692                     width:400, height:100,
34693                     buttonAlign:"center",
34694                     closeClick : function(){
34695                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34696                             handleButton("no");
34697                         }else{
34698                             handleButton("cancel");
34699                         }
34700                     }
34701                 });
34702                 dlg.on("hide", handleHide);
34703                 mask = dlg.mask;
34704                 dlg.addKeyListener(27, handleEsc);
34705                 buttons = {};
34706                 var bt = this.buttonText;
34707                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34708                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34709                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34710                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34711                 bodyEl = dlg.body.createChild({
34712
34713                     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>'
34714                 });
34715                 msgEl = bodyEl.dom.firstChild;
34716                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34717                 textboxEl.enableDisplayMode();
34718                 textboxEl.addKeyListener([10,13], function(){
34719                     if(dlg.isVisible() && opt && opt.buttons){
34720                         if(opt.buttons.ok){
34721                             handleButton("ok");
34722                         }else if(opt.buttons.yes){
34723                             handleButton("yes");
34724                         }
34725                     }
34726                 });
34727                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34728                 textareaEl.enableDisplayMode();
34729                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34730                 progressEl.enableDisplayMode();
34731                 var pf = progressEl.dom.firstChild;
34732                 if (pf) {
34733                     pp = Roo.get(pf.firstChild);
34734                     pp.setHeight(pf.offsetHeight);
34735                 }
34736                 
34737             }
34738             return dlg;
34739         },
34740
34741         /**
34742          * Updates the message box body text
34743          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34744          * the XHTML-compliant non-breaking space character '&amp;#160;')
34745          * @return {Roo.MessageBox} This message box
34746          */
34747         updateText : function(text){
34748             if(!dlg.isVisible() && !opt.width){
34749                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34750             }
34751             msgEl.innerHTML = text || '&#160;';
34752       
34753             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34754             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34755             var w = Math.max(
34756                     Math.min(opt.width || cw , this.maxWidth), 
34757                     Math.max(opt.minWidth || this.minWidth, bwidth)
34758             );
34759             if(opt.prompt){
34760                 activeTextEl.setWidth(w);
34761             }
34762             if(dlg.isVisible()){
34763                 dlg.fixedcenter = false;
34764             }
34765             // to big, make it scroll. = But as usual stupid IE does not support
34766             // !important..
34767             
34768             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34769                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34770                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34771             } else {
34772                 bodyEl.dom.style.height = '';
34773                 bodyEl.dom.style.overflowY = '';
34774             }
34775             if (cw > w) {
34776                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34777             } else {
34778                 bodyEl.dom.style.overflowX = '';
34779             }
34780             
34781             dlg.setContentSize(w, bodyEl.getHeight());
34782             if(dlg.isVisible()){
34783                 dlg.fixedcenter = true;
34784             }
34785             return this;
34786         },
34787
34788         /**
34789          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34790          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34791          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34792          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34793          * @return {Roo.MessageBox} This message box
34794          */
34795         updateProgress : function(value, text){
34796             if(text){
34797                 this.updateText(text);
34798             }
34799             if (pp) { // weird bug on my firefox - for some reason this is not defined
34800                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34801             }
34802             return this;
34803         },        
34804
34805         /**
34806          * Returns true if the message box is currently displayed
34807          * @return {Boolean} True if the message box is visible, else false
34808          */
34809         isVisible : function(){
34810             return dlg && dlg.isVisible();  
34811         },
34812
34813         /**
34814          * Hides the message box if it is displayed
34815          */
34816         hide : function(){
34817             if(this.isVisible()){
34818                 dlg.hide();
34819             }  
34820         },
34821
34822         /**
34823          * Displays a new message box, or reinitializes an existing message box, based on the config options
34824          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34825          * The following config object properties are supported:
34826          * <pre>
34827 Property    Type             Description
34828 ----------  ---------------  ------------------------------------------------------------------------------------
34829 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34830                                    closes (defaults to undefined)
34831 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34832                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34833 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34834                                    progress and wait dialogs will ignore this property and always hide the
34835                                    close button as they can only be closed programmatically.
34836 cls               String           A custom CSS class to apply to the message box element
34837 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34838                                    displayed (defaults to 75)
34839 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34840                                    function will be btn (the name of the button that was clicked, if applicable,
34841                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34842                                    Progress and wait dialogs will ignore this option since they do not respond to
34843                                    user actions and can only be closed programmatically, so any required function
34844                                    should be called by the same code after it closes the dialog.
34845 icon              String           A CSS class that provides a background image to be used as an icon for
34846                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34847 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34848 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34849 modal             Boolean          False to allow user interaction with the page while the message box is
34850                                    displayed (defaults to true)
34851 msg               String           A string that will replace the existing message box body text (defaults
34852                                    to the XHTML-compliant non-breaking space character '&#160;')
34853 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34854 progress          Boolean          True to display a progress bar (defaults to false)
34855 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34856 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34857 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34858 title             String           The title text
34859 value             String           The string value to set into the active textbox element if displayed
34860 wait              Boolean          True to display a progress bar (defaults to false)
34861 width             Number           The width of the dialog in pixels
34862 </pre>
34863          *
34864          * Example usage:
34865          * <pre><code>
34866 Roo.Msg.show({
34867    title: 'Address',
34868    msg: 'Please enter your address:',
34869    width: 300,
34870    buttons: Roo.MessageBox.OKCANCEL,
34871    multiline: true,
34872    fn: saveAddress,
34873    animEl: 'addAddressBtn'
34874 });
34875 </code></pre>
34876          * @param {Object} config Configuration options
34877          * @return {Roo.MessageBox} This message box
34878          */
34879         show : function(options)
34880         {
34881             
34882             // this causes nightmares if you show one dialog after another
34883             // especially on callbacks..
34884              
34885             if(this.isVisible()){
34886                 
34887                 this.hide();
34888                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34889                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34890                 Roo.log("New Dialog Message:" +  options.msg )
34891                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34892                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34893                 
34894             }
34895             var d = this.getDialog();
34896             opt = options;
34897             d.setTitle(opt.title || "&#160;");
34898             d.close.setDisplayed(opt.closable !== false);
34899             activeTextEl = textboxEl;
34900             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34901             if(opt.prompt){
34902                 if(opt.multiline){
34903                     textboxEl.hide();
34904                     textareaEl.show();
34905                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34906                         opt.multiline : this.defaultTextHeight);
34907                     activeTextEl = textareaEl;
34908                 }else{
34909                     textboxEl.show();
34910                     textareaEl.hide();
34911                 }
34912             }else{
34913                 textboxEl.hide();
34914                 textareaEl.hide();
34915             }
34916             progressEl.setDisplayed(opt.progress === true);
34917             this.updateProgress(0);
34918             activeTextEl.dom.value = opt.value || "";
34919             if(opt.prompt){
34920                 dlg.setDefaultButton(activeTextEl);
34921             }else{
34922                 var bs = opt.buttons;
34923                 var db = null;
34924                 if(bs && bs.ok){
34925                     db = buttons["ok"];
34926                 }else if(bs && bs.yes){
34927                     db = buttons["yes"];
34928                 }
34929                 dlg.setDefaultButton(db);
34930             }
34931             bwidth = updateButtons(opt.buttons);
34932             this.updateText(opt.msg);
34933             if(opt.cls){
34934                 d.el.addClass(opt.cls);
34935             }
34936             d.proxyDrag = opt.proxyDrag === true;
34937             d.modal = opt.modal !== false;
34938             d.mask = opt.modal !== false ? mask : false;
34939             if(!d.isVisible()){
34940                 // force it to the end of the z-index stack so it gets a cursor in FF
34941                 document.body.appendChild(dlg.el.dom);
34942                 d.animateTarget = null;
34943                 d.show(options.animEl);
34944             }
34945             return this;
34946         },
34947
34948         /**
34949          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34950          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34951          * and closing the message box when the process is complete.
34952          * @param {String} title The title bar text
34953          * @param {String} msg The message box body text
34954          * @return {Roo.MessageBox} This message box
34955          */
34956         progress : function(title, msg){
34957             this.show({
34958                 title : title,
34959                 msg : msg,
34960                 buttons: false,
34961                 progress:true,
34962                 closable:false,
34963                 minWidth: this.minProgressWidth,
34964                 modal : true
34965             });
34966             return this;
34967         },
34968
34969         /**
34970          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34971          * If a callback function is passed it will be called after the user clicks the button, and the
34972          * id of the button that was clicked will be passed as the only parameter to the callback
34973          * (could also be the top-right close button).
34974          * @param {String} title The title bar text
34975          * @param {String} msg The message box body text
34976          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34977          * @param {Object} scope (optional) The scope of the callback function
34978          * @return {Roo.MessageBox} This message box
34979          */
34980         alert : function(title, msg, fn, scope){
34981             this.show({
34982                 title : title,
34983                 msg : msg,
34984                 buttons: this.OK,
34985                 fn: fn,
34986                 scope : scope,
34987                 modal : true
34988             });
34989             return this;
34990         },
34991
34992         /**
34993          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34994          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34995          * You are responsible for closing the message box when the process is complete.
34996          * @param {String} msg The message box body text
34997          * @param {String} title (optional) The title bar text
34998          * @return {Roo.MessageBox} This message box
34999          */
35000         wait : function(msg, title){
35001             this.show({
35002                 title : title,
35003                 msg : msg,
35004                 buttons: false,
35005                 closable:false,
35006                 progress:true,
35007                 modal:true,
35008                 width:300,
35009                 wait:true
35010             });
35011             waitTimer = Roo.TaskMgr.start({
35012                 run: function(i){
35013                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
35014                 },
35015                 interval: 1000
35016             });
35017             return this;
35018         },
35019
35020         /**
35021          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
35022          * If a callback function is passed it will be called after the user clicks either button, and the id of the
35023          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
35024          * @param {String} title The title bar text
35025          * @param {String} msg The message box body text
35026          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35027          * @param {Object} scope (optional) The scope of the callback function
35028          * @return {Roo.MessageBox} This message box
35029          */
35030         confirm : function(title, msg, fn, scope){
35031             this.show({
35032                 title : title,
35033                 msg : msg,
35034                 buttons: this.YESNO,
35035                 fn: fn,
35036                 scope : scope,
35037                 modal : true
35038             });
35039             return this;
35040         },
35041
35042         /**
35043          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
35044          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
35045          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
35046          * (could also be the top-right close button) and the text that was entered will be passed as the two
35047          * parameters to the callback.
35048          * @param {String} title The title bar text
35049          * @param {String} msg The message box body text
35050          * @param {Function} fn (optional) The callback function invoked after the message box is closed
35051          * @param {Object} scope (optional) The scope of the callback function
35052          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
35053          * property, or the height in pixels to create the textbox (defaults to false / single-line)
35054          * @return {Roo.MessageBox} This message box
35055          */
35056         prompt : function(title, msg, fn, scope, multiline){
35057             this.show({
35058                 title : title,
35059                 msg : msg,
35060                 buttons: this.OKCANCEL,
35061                 fn: fn,
35062                 minWidth:250,
35063                 scope : scope,
35064                 prompt:true,
35065                 multiline: multiline,
35066                 modal : true
35067             });
35068             return this;
35069         },
35070
35071         /**
35072          * Button config that displays a single OK button
35073          * @type Object
35074          */
35075         OK : {ok:true},
35076         /**
35077          * Button config that displays Yes and No buttons
35078          * @type Object
35079          */
35080         YESNO : {yes:true, no:true},
35081         /**
35082          * Button config that displays OK and Cancel buttons
35083          * @type Object
35084          */
35085         OKCANCEL : {ok:true, cancel:true},
35086         /**
35087          * Button config that displays Yes, No and Cancel buttons
35088          * @type Object
35089          */
35090         YESNOCANCEL : {yes:true, no:true, cancel:true},
35091
35092         /**
35093          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
35094          * @type Number
35095          */
35096         defaultTextHeight : 75,
35097         /**
35098          * The maximum width in pixels of the message box (defaults to 600)
35099          * @type Number
35100          */
35101         maxWidth : 600,
35102         /**
35103          * The minimum width in pixels of the message box (defaults to 100)
35104          * @type Number
35105          */
35106         minWidth : 100,
35107         /**
35108          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
35109          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
35110          * @type Number
35111          */
35112         minProgressWidth : 250,
35113         /**
35114          * An object containing the default button text strings that can be overriden for localized language support.
35115          * Supported properties are: ok, cancel, yes and no.
35116          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
35117          * @type Object
35118          */
35119         buttonText : {
35120             ok : "OK",
35121             cancel : "Cancel",
35122             yes : "Yes",
35123             no : "No"
35124         }
35125     };
35126 }();
35127
35128 /**
35129  * Shorthand for {@link Roo.MessageBox}
35130  */
35131 Roo.Msg = Roo.MessageBox;/*
35132  * Based on:
35133  * Ext JS Library 1.1.1
35134  * Copyright(c) 2006-2007, Ext JS, LLC.
35135  *
35136  * Originally Released Under LGPL - original licence link has changed is not relivant.
35137  *
35138  * Fork - LGPL
35139  * <script type="text/javascript">
35140  */
35141 /**
35142  * @class Roo.QuickTips
35143  * Provides attractive and customizable tooltips for any element.
35144  * @static
35145  */
35146 Roo.QuickTips = function(){
35147     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
35148     var ce, bd, xy, dd;
35149     var visible = false, disabled = true, inited = false;
35150     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
35151     
35152     var onOver = function(e){
35153         if(disabled){
35154             return;
35155         }
35156         var t = e.getTarget();
35157         if(!t || t.nodeType !== 1 || t == document || t == document.body){
35158             return;
35159         }
35160         if(ce && t == ce.el){
35161             clearTimeout(hideProc);
35162             return;
35163         }
35164         if(t && tagEls[t.id]){
35165             tagEls[t.id].el = t;
35166             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
35167             return;
35168         }
35169         var ttp, et = Roo.fly(t);
35170         var ns = cfg.namespace;
35171         if(tm.interceptTitles && t.title){
35172             ttp = t.title;
35173             t.qtip = ttp;
35174             t.removeAttribute("title");
35175             e.preventDefault();
35176         }else{
35177             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
35178         }
35179         if(ttp){
35180             showProc = show.defer(tm.showDelay, tm, [{
35181                 el: t, 
35182                 text: ttp.replace(/\\n/g,'<br/>'),
35183                 width: et.getAttributeNS(ns, cfg.width),
35184                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
35185                 title: et.getAttributeNS(ns, cfg.title),
35186                     cls: et.getAttributeNS(ns, cfg.cls)
35187             }]);
35188         }
35189     };
35190     
35191     var onOut = function(e){
35192         clearTimeout(showProc);
35193         var t = e.getTarget();
35194         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
35195             hideProc = setTimeout(hide, tm.hideDelay);
35196         }
35197     };
35198     
35199     var onMove = function(e){
35200         if(disabled){
35201             return;
35202         }
35203         xy = e.getXY();
35204         xy[1] += 18;
35205         if(tm.trackMouse && ce){
35206             el.setXY(xy);
35207         }
35208     };
35209     
35210     var onDown = function(e){
35211         clearTimeout(showProc);
35212         clearTimeout(hideProc);
35213         if(!e.within(el)){
35214             if(tm.hideOnClick){
35215                 hide();
35216                 tm.disable();
35217                 tm.enable.defer(100, tm);
35218             }
35219         }
35220     };
35221     
35222     var getPad = function(){
35223         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
35224     };
35225
35226     var show = function(o){
35227         if(disabled){
35228             return;
35229         }
35230         clearTimeout(dismissProc);
35231         ce = o;
35232         if(removeCls){ // in case manually hidden
35233             el.removeClass(removeCls);
35234             removeCls = null;
35235         }
35236         if(ce.cls){
35237             el.addClass(ce.cls);
35238             removeCls = ce.cls;
35239         }
35240         if(ce.title){
35241             tipTitle.update(ce.title);
35242             tipTitle.show();
35243         }else{
35244             tipTitle.update('');
35245             tipTitle.hide();
35246         }
35247         el.dom.style.width  = tm.maxWidth+'px';
35248         //tipBody.dom.style.width = '';
35249         tipBodyText.update(o.text);
35250         var p = getPad(), w = ce.width;
35251         if(!w){
35252             var td = tipBodyText.dom;
35253             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
35254             if(aw > tm.maxWidth){
35255                 w = tm.maxWidth;
35256             }else if(aw < tm.minWidth){
35257                 w = tm.minWidth;
35258             }else{
35259                 w = aw;
35260             }
35261         }
35262         //tipBody.setWidth(w);
35263         el.setWidth(parseInt(w, 10) + p);
35264         if(ce.autoHide === false){
35265             close.setDisplayed(true);
35266             if(dd){
35267                 dd.unlock();
35268             }
35269         }else{
35270             close.setDisplayed(false);
35271             if(dd){
35272                 dd.lock();
35273             }
35274         }
35275         if(xy){
35276             el.avoidY = xy[1]-18;
35277             el.setXY(xy);
35278         }
35279         if(tm.animate){
35280             el.setOpacity(.1);
35281             el.setStyle("visibility", "visible");
35282             el.fadeIn({callback: afterShow});
35283         }else{
35284             afterShow();
35285         }
35286     };
35287     
35288     var afterShow = function(){
35289         if(ce){
35290             el.show();
35291             esc.enable();
35292             if(tm.autoDismiss && ce.autoHide !== false){
35293                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
35294             }
35295         }
35296     };
35297     
35298     var hide = function(noanim){
35299         clearTimeout(dismissProc);
35300         clearTimeout(hideProc);
35301         ce = null;
35302         if(el.isVisible()){
35303             esc.disable();
35304             if(noanim !== true && tm.animate){
35305                 el.fadeOut({callback: afterHide});
35306             }else{
35307                 afterHide();
35308             } 
35309         }
35310     };
35311     
35312     var afterHide = function(){
35313         el.hide();
35314         if(removeCls){
35315             el.removeClass(removeCls);
35316             removeCls = null;
35317         }
35318     };
35319     
35320     return {
35321         /**
35322         * @cfg {Number} minWidth
35323         * The minimum width of the quick tip (defaults to 40)
35324         */
35325        minWidth : 40,
35326         /**
35327         * @cfg {Number} maxWidth
35328         * The maximum width of the quick tip (defaults to 300)
35329         */
35330        maxWidth : 300,
35331         /**
35332         * @cfg {Boolean} interceptTitles
35333         * True to automatically use the element's DOM title value if available (defaults to false)
35334         */
35335        interceptTitles : false,
35336         /**
35337         * @cfg {Boolean} trackMouse
35338         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
35339         */
35340        trackMouse : false,
35341         /**
35342         * @cfg {Boolean} hideOnClick
35343         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
35344         */
35345        hideOnClick : true,
35346         /**
35347         * @cfg {Number} showDelay
35348         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35349         */
35350        showDelay : 500,
35351         /**
35352         * @cfg {Number} hideDelay
35353         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35354         */
35355        hideDelay : 200,
35356         /**
35357         * @cfg {Boolean} autoHide
35358         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35359         * Used in conjunction with hideDelay.
35360         */
35361        autoHide : true,
35362         /**
35363         * @cfg {Boolean}
35364         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35365         * (defaults to true).  Used in conjunction with autoDismissDelay.
35366         */
35367        autoDismiss : true,
35368         /**
35369         * @cfg {Number}
35370         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35371         */
35372        autoDismissDelay : 5000,
35373        /**
35374         * @cfg {Boolean} animate
35375         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35376         */
35377        animate : false,
35378
35379        /**
35380         * @cfg {String} title
35381         * Title text to display (defaults to '').  This can be any valid HTML markup.
35382         */
35383         title: '',
35384        /**
35385         * @cfg {String} text
35386         * Body text to display (defaults to '').  This can be any valid HTML markup.
35387         */
35388         text : '',
35389        /**
35390         * @cfg {String} cls
35391         * A CSS class to apply to the base quick tip element (defaults to '').
35392         */
35393         cls : '',
35394        /**
35395         * @cfg {Number} width
35396         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35397         * minWidth or maxWidth.
35398         */
35399         width : null,
35400
35401     /**
35402      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35403      * or display QuickTips in a page.
35404      */
35405        init : function(){
35406           tm = Roo.QuickTips;
35407           cfg = tm.tagConfig;
35408           if(!inited){
35409               if(!Roo.isReady){ // allow calling of init() before onReady
35410                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35411                   return;
35412               }
35413               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35414               el.fxDefaults = {stopFx: true};
35415               // maximum custom styling
35416               //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>');
35417               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>');              
35418               tipTitle = el.child('h3');
35419               tipTitle.enableDisplayMode("block");
35420               tipBody = el.child('div.x-tip-bd');
35421               tipBodyText = el.child('div.x-tip-bd-inner');
35422               //bdLeft = el.child('div.x-tip-bd-left');
35423               //bdRight = el.child('div.x-tip-bd-right');
35424               close = el.child('div.x-tip-close');
35425               close.enableDisplayMode("block");
35426               close.on("click", hide);
35427               var d = Roo.get(document);
35428               d.on("mousedown", onDown);
35429               d.on("mouseover", onOver);
35430               d.on("mouseout", onOut);
35431               d.on("mousemove", onMove);
35432               esc = d.addKeyListener(27, hide);
35433               esc.disable();
35434               if(Roo.dd.DD){
35435                   dd = el.initDD("default", null, {
35436                       onDrag : function(){
35437                           el.sync();  
35438                       }
35439                   });
35440                   dd.setHandleElId(tipTitle.id);
35441                   dd.lock();
35442               }
35443               inited = true;
35444           }
35445           this.enable(); 
35446        },
35447
35448     /**
35449      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35450      * are supported:
35451      * <pre>
35452 Property    Type                   Description
35453 ----------  ---------------------  ------------------------------------------------------------------------
35454 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35455      * </ul>
35456      * @param {Object} config The config object
35457      */
35458        register : function(config){
35459            var cs = config instanceof Array ? config : arguments;
35460            for(var i = 0, len = cs.length; i < len; i++) {
35461                var c = cs[i];
35462                var target = c.target;
35463                if(target){
35464                    if(target instanceof Array){
35465                        for(var j = 0, jlen = target.length; j < jlen; j++){
35466                            tagEls[target[j]] = c;
35467                        }
35468                    }else{
35469                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35470                    }
35471                }
35472            }
35473        },
35474
35475     /**
35476      * Removes this quick tip from its element and destroys it.
35477      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35478      */
35479        unregister : function(el){
35480            delete tagEls[Roo.id(el)];
35481        },
35482
35483     /**
35484      * Enable this quick tip.
35485      */
35486        enable : function(){
35487            if(inited && disabled){
35488                locks.pop();
35489                if(locks.length < 1){
35490                    disabled = false;
35491                }
35492            }
35493        },
35494
35495     /**
35496      * Disable this quick tip.
35497      */
35498        disable : function(){
35499           disabled = true;
35500           clearTimeout(showProc);
35501           clearTimeout(hideProc);
35502           clearTimeout(dismissProc);
35503           if(ce){
35504               hide(true);
35505           }
35506           locks.push(1);
35507        },
35508
35509     /**
35510      * Returns true if the quick tip is enabled, else false.
35511      */
35512        isEnabled : function(){
35513             return !disabled;
35514        },
35515
35516         // private
35517        tagConfig : {
35518            namespace : "roo", // was ext?? this may break..
35519            alt_namespace : "ext",
35520            attribute : "qtip",
35521            width : "width",
35522            target : "target",
35523            title : "qtitle",
35524            hide : "hide",
35525            cls : "qclass"
35526        }
35527    };
35528 }();
35529
35530 // backwards compat
35531 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35532  * Based on:
35533  * Ext JS Library 1.1.1
35534  * Copyright(c) 2006-2007, Ext JS, LLC.
35535  *
35536  * Originally Released Under LGPL - original licence link has changed is not relivant.
35537  *
35538  * Fork - LGPL
35539  * <script type="text/javascript">
35540  */
35541  
35542
35543 /**
35544  * @class Roo.tree.TreePanel
35545  * @extends Roo.data.Tree
35546  * @cfg {Roo.tree.TreeNode} root The root node
35547  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35548  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35549  * @cfg {Boolean} enableDD true to enable drag and drop
35550  * @cfg {Boolean} enableDrag true to enable just drag
35551  * @cfg {Boolean} enableDrop true to enable just drop
35552  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35553  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35554  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35555  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35556  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35557  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35558  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35559  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35560  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35561  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35562  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35563  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35564  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35565  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35566  * @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>
35567  * @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>
35568  * 
35569  * @constructor
35570  * @param {String/HTMLElement/Element} el The container element
35571  * @param {Object} config
35572  */
35573 Roo.tree.TreePanel = function(el, config){
35574     var root = false;
35575     var loader = false;
35576     if (config.root) {
35577         root = config.root;
35578         delete config.root;
35579     }
35580     if (config.loader) {
35581         loader = config.loader;
35582         delete config.loader;
35583     }
35584     
35585     Roo.apply(this, config);
35586     Roo.tree.TreePanel.superclass.constructor.call(this);
35587     this.el = Roo.get(el);
35588     this.el.addClass('x-tree');
35589     //console.log(root);
35590     if (root) {
35591         this.setRootNode( Roo.factory(root, Roo.tree));
35592     }
35593     if (loader) {
35594         this.loader = Roo.factory(loader, Roo.tree);
35595     }
35596    /**
35597     * Read-only. The id of the container element becomes this TreePanel's id.
35598     */
35599     this.id = this.el.id;
35600     this.addEvents({
35601         /**
35602         * @event beforeload
35603         * Fires before a node is loaded, return false to cancel
35604         * @param {Node} node The node being loaded
35605         */
35606         "beforeload" : true,
35607         /**
35608         * @event load
35609         * Fires when a node is loaded
35610         * @param {Node} node The node that was loaded
35611         */
35612         "load" : true,
35613         /**
35614         * @event textchange
35615         * Fires when the text for a node is changed
35616         * @param {Node} node The node
35617         * @param {String} text The new text
35618         * @param {String} oldText The old text
35619         */
35620         "textchange" : true,
35621         /**
35622         * @event beforeexpand
35623         * Fires before a node is expanded, return false to cancel.
35624         * @param {Node} node The node
35625         * @param {Boolean} deep
35626         * @param {Boolean} anim
35627         */
35628         "beforeexpand" : true,
35629         /**
35630         * @event beforecollapse
35631         * Fires before a node is collapsed, return false to cancel.
35632         * @param {Node} node The node
35633         * @param {Boolean} deep
35634         * @param {Boolean} anim
35635         */
35636         "beforecollapse" : true,
35637         /**
35638         * @event expand
35639         * Fires when a node is expanded
35640         * @param {Node} node The node
35641         */
35642         "expand" : true,
35643         /**
35644         * @event disabledchange
35645         * Fires when the disabled status of a node changes
35646         * @param {Node} node The node
35647         * @param {Boolean} disabled
35648         */
35649         "disabledchange" : true,
35650         /**
35651         * @event collapse
35652         * Fires when a node is collapsed
35653         * @param {Node} node The node
35654         */
35655         "collapse" : true,
35656         /**
35657         * @event beforeclick
35658         * Fires before click processing on a node. Return false to cancel the default action.
35659         * @param {Node} node The node
35660         * @param {Roo.EventObject} e The event object
35661         */
35662         "beforeclick":true,
35663         /**
35664         * @event checkchange
35665         * Fires when a node with a checkbox's checked property changes
35666         * @param {Node} this This node
35667         * @param {Boolean} checked
35668         */
35669         "checkchange":true,
35670         /**
35671         * @event click
35672         * Fires when a node is clicked
35673         * @param {Node} node The node
35674         * @param {Roo.EventObject} e The event object
35675         */
35676         "click":true,
35677         /**
35678         * @event dblclick
35679         * Fires when a node is double clicked
35680         * @param {Node} node The node
35681         * @param {Roo.EventObject} e The event object
35682         */
35683         "dblclick":true,
35684         /**
35685         * @event contextmenu
35686         * Fires when a node is right clicked
35687         * @param {Node} node The node
35688         * @param {Roo.EventObject} e The event object
35689         */
35690         "contextmenu":true,
35691         /**
35692         * @event beforechildrenrendered
35693         * Fires right before the child nodes for a node are rendered
35694         * @param {Node} node The node
35695         */
35696         "beforechildrenrendered":true,
35697         /**
35698         * @event startdrag
35699         * Fires when a node starts being dragged
35700         * @param {Roo.tree.TreePanel} this
35701         * @param {Roo.tree.TreeNode} node
35702         * @param {event} e The raw browser event
35703         */ 
35704        "startdrag" : true,
35705        /**
35706         * @event enddrag
35707         * Fires when a drag operation is complete
35708         * @param {Roo.tree.TreePanel} this
35709         * @param {Roo.tree.TreeNode} node
35710         * @param {event} e The raw browser event
35711         */
35712        "enddrag" : true,
35713        /**
35714         * @event dragdrop
35715         * Fires when a dragged node is dropped on a valid DD target
35716         * @param {Roo.tree.TreePanel} this
35717         * @param {Roo.tree.TreeNode} node
35718         * @param {DD} dd The dd it was dropped on
35719         * @param {event} e The raw browser event
35720         */
35721        "dragdrop" : true,
35722        /**
35723         * @event beforenodedrop
35724         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35725         * passed to handlers has the following properties:<br />
35726         * <ul style="padding:5px;padding-left:16px;">
35727         * <li>tree - The TreePanel</li>
35728         * <li>target - The node being targeted for the drop</li>
35729         * <li>data - The drag data from the drag source</li>
35730         * <li>point - The point of the drop - append, above or below</li>
35731         * <li>source - The drag source</li>
35732         * <li>rawEvent - Raw mouse event</li>
35733         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35734         * to be inserted by setting them on this object.</li>
35735         * <li>cancel - Set this to true to cancel the drop.</li>
35736         * </ul>
35737         * @param {Object} dropEvent
35738         */
35739        "beforenodedrop" : true,
35740        /**
35741         * @event nodedrop
35742         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35743         * passed to handlers has the following properties:<br />
35744         * <ul style="padding:5px;padding-left:16px;">
35745         * <li>tree - The TreePanel</li>
35746         * <li>target - The node being targeted for the drop</li>
35747         * <li>data - The drag data from the drag source</li>
35748         * <li>point - The point of the drop - append, above or below</li>
35749         * <li>source - The drag source</li>
35750         * <li>rawEvent - Raw mouse event</li>
35751         * <li>dropNode - Dropped node(s).</li>
35752         * </ul>
35753         * @param {Object} dropEvent
35754         */
35755        "nodedrop" : true,
35756         /**
35757         * @event nodedragover
35758         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
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 - Drop node(s) provided by the source.</li>
35768         * <li>cancel - Set this to true to signal drop not allowed.</li>
35769         * </ul>
35770         * @param {Object} dragOverEvent
35771         */
35772        "nodedragover" : true,
35773        /**
35774         * @event appendnode
35775         * Fires when append node to the tree
35776         * @param {Roo.tree.TreePanel} this
35777         * @param {Roo.tree.TreeNode} node
35778         * @param {Number} index The index of the newly appended node
35779         */
35780        "appendnode" : true
35781         
35782     });
35783     if(this.singleExpand){
35784        this.on("beforeexpand", this.restrictExpand, this);
35785     }
35786     if (this.editor) {
35787         this.editor.tree = this;
35788         this.editor = Roo.factory(this.editor, Roo.tree);
35789     }
35790     
35791     if (this.selModel) {
35792         this.selModel = Roo.factory(this.selModel, Roo.tree);
35793     }
35794    
35795 };
35796 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35797     rootVisible : true,
35798     animate: Roo.enableFx,
35799     lines : true,
35800     enableDD : false,
35801     hlDrop : Roo.enableFx,
35802   
35803     renderer: false,
35804     
35805     rendererTip: false,
35806     // private
35807     restrictExpand : function(node){
35808         var p = node.parentNode;
35809         if(p){
35810             if(p.expandedChild && p.expandedChild.parentNode == p){
35811                 p.expandedChild.collapse();
35812             }
35813             p.expandedChild = node;
35814         }
35815     },
35816
35817     // private override
35818     setRootNode : function(node){
35819         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35820         if(!this.rootVisible){
35821             node.ui = new Roo.tree.RootTreeNodeUI(node);
35822         }
35823         return node;
35824     },
35825
35826     /**
35827      * Returns the container element for this TreePanel
35828      */
35829     getEl : function(){
35830         return this.el;
35831     },
35832
35833     /**
35834      * Returns the default TreeLoader for this TreePanel
35835      */
35836     getLoader : function(){
35837         return this.loader;
35838     },
35839
35840     /**
35841      * Expand all nodes
35842      */
35843     expandAll : function(){
35844         this.root.expand(true);
35845     },
35846
35847     /**
35848      * Collapse all nodes
35849      */
35850     collapseAll : function(){
35851         this.root.collapse(true);
35852     },
35853
35854     /**
35855      * Returns the selection model used by this TreePanel
35856      */
35857     getSelectionModel : function(){
35858         if(!this.selModel){
35859             this.selModel = new Roo.tree.DefaultSelectionModel();
35860         }
35861         return this.selModel;
35862     },
35863
35864     /**
35865      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35866      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35867      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35868      * @return {Array}
35869      */
35870     getChecked : function(a, startNode){
35871         startNode = startNode || this.root;
35872         var r = [];
35873         var f = function(){
35874             if(this.attributes.checked){
35875                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35876             }
35877         }
35878         startNode.cascade(f);
35879         return r;
35880     },
35881
35882     /**
35883      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35884      * @param {String} path
35885      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35886      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35887      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35888      */
35889     expandPath : function(path, attr, callback){
35890         attr = attr || "id";
35891         var keys = path.split(this.pathSeparator);
35892         var curNode = this.root;
35893         if(curNode.attributes[attr] != keys[1]){ // invalid root
35894             if(callback){
35895                 callback(false, null);
35896             }
35897             return;
35898         }
35899         var index = 1;
35900         var f = function(){
35901             if(++index == keys.length){
35902                 if(callback){
35903                     callback(true, curNode);
35904                 }
35905                 return;
35906             }
35907             var c = curNode.findChild(attr, keys[index]);
35908             if(!c){
35909                 if(callback){
35910                     callback(false, curNode);
35911                 }
35912                 return;
35913             }
35914             curNode = c;
35915             c.expand(false, false, f);
35916         };
35917         curNode.expand(false, false, f);
35918     },
35919
35920     /**
35921      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35922      * @param {String} path
35923      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35924      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35925      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35926      */
35927     selectPath : function(path, attr, callback){
35928         attr = attr || "id";
35929         var keys = path.split(this.pathSeparator);
35930         var v = keys.pop();
35931         if(keys.length > 0){
35932             var f = function(success, node){
35933                 if(success && node){
35934                     var n = node.findChild(attr, v);
35935                     if(n){
35936                         n.select();
35937                         if(callback){
35938                             callback(true, n);
35939                         }
35940                     }else if(callback){
35941                         callback(false, n);
35942                     }
35943                 }else{
35944                     if(callback){
35945                         callback(false, n);
35946                     }
35947                 }
35948             };
35949             this.expandPath(keys.join(this.pathSeparator), attr, f);
35950         }else{
35951             this.root.select();
35952             if(callback){
35953                 callback(true, this.root);
35954             }
35955         }
35956     },
35957
35958     getTreeEl : function(){
35959         return this.el;
35960     },
35961
35962     /**
35963      * Trigger rendering of this TreePanel
35964      */
35965     render : function(){
35966         if (this.innerCt) {
35967             return this; // stop it rendering more than once!!
35968         }
35969         
35970         this.innerCt = this.el.createChild({tag:"ul",
35971                cls:"x-tree-root-ct " +
35972                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35973
35974         if(this.containerScroll){
35975             Roo.dd.ScrollManager.register(this.el);
35976         }
35977         if((this.enableDD || this.enableDrop) && !this.dropZone){
35978            /**
35979             * The dropZone used by this tree if drop is enabled
35980             * @type Roo.tree.TreeDropZone
35981             */
35982              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35983                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35984            });
35985         }
35986         if((this.enableDD || this.enableDrag) && !this.dragZone){
35987            /**
35988             * The dragZone used by this tree if drag is enabled
35989             * @type Roo.tree.TreeDragZone
35990             */
35991             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35992                ddGroup: this.ddGroup || "TreeDD",
35993                scroll: this.ddScroll
35994            });
35995         }
35996         this.getSelectionModel().init(this);
35997         if (!this.root) {
35998             Roo.log("ROOT not set in tree");
35999             return this;
36000         }
36001         this.root.render();
36002         if(!this.rootVisible){
36003             this.root.renderChildren();
36004         }
36005         return this;
36006     }
36007 });/*
36008  * Based on:
36009  * Ext JS Library 1.1.1
36010  * Copyright(c) 2006-2007, Ext JS, LLC.
36011  *
36012  * Originally Released Under LGPL - original licence link has changed is not relivant.
36013  *
36014  * Fork - LGPL
36015  * <script type="text/javascript">
36016  */
36017  
36018
36019 /**
36020  * @class Roo.tree.DefaultSelectionModel
36021  * @extends Roo.util.Observable
36022  * The default single selection for a TreePanel.
36023  * @param {Object} cfg Configuration
36024  */
36025 Roo.tree.DefaultSelectionModel = function(cfg){
36026    this.selNode = null;
36027    
36028    
36029    
36030    this.addEvents({
36031        /**
36032         * @event selectionchange
36033         * Fires when the selected node changes
36034         * @param {DefaultSelectionModel} this
36035         * @param {TreeNode} node the new selection
36036         */
36037        "selectionchange" : true,
36038
36039        /**
36040         * @event beforeselect
36041         * Fires before the selected node changes, return false to cancel the change
36042         * @param {DefaultSelectionModel} this
36043         * @param {TreeNode} node the new selection
36044         * @param {TreeNode} node the old selection
36045         */
36046        "beforeselect" : true
36047    });
36048    
36049     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
36050 };
36051
36052 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
36053     init : function(tree){
36054         this.tree = tree;
36055         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36056         tree.on("click", this.onNodeClick, this);
36057     },
36058     
36059     onNodeClick : function(node, e){
36060         if (e.ctrlKey && this.selNode == node)  {
36061             this.unselect(node);
36062             return;
36063         }
36064         this.select(node);
36065     },
36066     
36067     /**
36068      * Select a node.
36069      * @param {TreeNode} node The node to select
36070      * @return {TreeNode} The selected node
36071      */
36072     select : function(node){
36073         var last = this.selNode;
36074         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
36075             if(last){
36076                 last.ui.onSelectedChange(false);
36077             }
36078             this.selNode = node;
36079             node.ui.onSelectedChange(true);
36080             this.fireEvent("selectionchange", this, node, last);
36081         }
36082         return node;
36083     },
36084     
36085     /**
36086      * Deselect a node.
36087      * @param {TreeNode} node The node to unselect
36088      */
36089     unselect : function(node){
36090         if(this.selNode == node){
36091             this.clearSelections();
36092         }    
36093     },
36094     
36095     /**
36096      * Clear all selections
36097      */
36098     clearSelections : function(){
36099         var n = this.selNode;
36100         if(n){
36101             n.ui.onSelectedChange(false);
36102             this.selNode = null;
36103             this.fireEvent("selectionchange", this, null);
36104         }
36105         return n;
36106     },
36107     
36108     /**
36109      * Get the selected node
36110      * @return {TreeNode} The selected node
36111      */
36112     getSelectedNode : function(){
36113         return this.selNode;    
36114     },
36115     
36116     /**
36117      * Returns true if the node is selected
36118      * @param {TreeNode} node The node to check
36119      * @return {Boolean}
36120      */
36121     isSelected : function(node){
36122         return this.selNode == node;  
36123     },
36124
36125     /**
36126      * Selects the node above the selected node in the tree, intelligently walking the nodes
36127      * @return TreeNode The new selection
36128      */
36129     selectPrevious : function(){
36130         var s = this.selNode || this.lastSelNode;
36131         if(!s){
36132             return null;
36133         }
36134         var ps = s.previousSibling;
36135         if(ps){
36136             if(!ps.isExpanded() || ps.childNodes.length < 1){
36137                 return this.select(ps);
36138             } else{
36139                 var lc = ps.lastChild;
36140                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
36141                     lc = lc.lastChild;
36142                 }
36143                 return this.select(lc);
36144             }
36145         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
36146             return this.select(s.parentNode);
36147         }
36148         return null;
36149     },
36150
36151     /**
36152      * Selects the node above the selected node in the tree, intelligently walking the nodes
36153      * @return TreeNode The new selection
36154      */
36155     selectNext : function(){
36156         var s = this.selNode || this.lastSelNode;
36157         if(!s){
36158             return null;
36159         }
36160         if(s.firstChild && s.isExpanded()){
36161              return this.select(s.firstChild);
36162          }else if(s.nextSibling){
36163              return this.select(s.nextSibling);
36164          }else if(s.parentNode){
36165             var newS = null;
36166             s.parentNode.bubble(function(){
36167                 if(this.nextSibling){
36168                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
36169                     return false;
36170                 }
36171             });
36172             return newS;
36173          }
36174         return null;
36175     },
36176
36177     onKeyDown : function(e){
36178         var s = this.selNode || this.lastSelNode;
36179         // undesirable, but required
36180         var sm = this;
36181         if(!s){
36182             return;
36183         }
36184         var k = e.getKey();
36185         switch(k){
36186              case e.DOWN:
36187                  e.stopEvent();
36188                  this.selectNext();
36189              break;
36190              case e.UP:
36191                  e.stopEvent();
36192                  this.selectPrevious();
36193              break;
36194              case e.RIGHT:
36195                  e.preventDefault();
36196                  if(s.hasChildNodes()){
36197                      if(!s.isExpanded()){
36198                          s.expand();
36199                      }else if(s.firstChild){
36200                          this.select(s.firstChild, e);
36201                      }
36202                  }
36203              break;
36204              case e.LEFT:
36205                  e.preventDefault();
36206                  if(s.hasChildNodes() && s.isExpanded()){
36207                      s.collapse();
36208                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
36209                      this.select(s.parentNode, e);
36210                  }
36211              break;
36212         };
36213     }
36214 });
36215
36216 /**
36217  * @class Roo.tree.MultiSelectionModel
36218  * @extends Roo.util.Observable
36219  * Multi selection for a TreePanel.
36220  * @param {Object} cfg Configuration
36221  */
36222 Roo.tree.MultiSelectionModel = function(){
36223    this.selNodes = [];
36224    this.selMap = {};
36225    this.addEvents({
36226        /**
36227         * @event selectionchange
36228         * Fires when the selected nodes change
36229         * @param {MultiSelectionModel} this
36230         * @param {Array} nodes Array of the selected nodes
36231         */
36232        "selectionchange" : true
36233    });
36234    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
36235    
36236 };
36237
36238 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
36239     init : function(tree){
36240         this.tree = tree;
36241         tree.getTreeEl().on("keydown", this.onKeyDown, this);
36242         tree.on("click", this.onNodeClick, this);
36243     },
36244     
36245     onNodeClick : function(node, e){
36246         this.select(node, e, e.ctrlKey);
36247     },
36248     
36249     /**
36250      * Select a node.
36251      * @param {TreeNode} node The node to select
36252      * @param {EventObject} e (optional) An event associated with the selection
36253      * @param {Boolean} keepExisting True to retain existing selections
36254      * @return {TreeNode} The selected node
36255      */
36256     select : function(node, e, keepExisting){
36257         if(keepExisting !== true){
36258             this.clearSelections(true);
36259         }
36260         if(this.isSelected(node)){
36261             this.lastSelNode = node;
36262             return node;
36263         }
36264         this.selNodes.push(node);
36265         this.selMap[node.id] = node;
36266         this.lastSelNode = node;
36267         node.ui.onSelectedChange(true);
36268         this.fireEvent("selectionchange", this, this.selNodes);
36269         return node;
36270     },
36271     
36272     /**
36273      * Deselect a node.
36274      * @param {TreeNode} node The node to unselect
36275      */
36276     unselect : function(node){
36277         if(this.selMap[node.id]){
36278             node.ui.onSelectedChange(false);
36279             var sn = this.selNodes;
36280             var index = -1;
36281             if(sn.indexOf){
36282                 index = sn.indexOf(node);
36283             }else{
36284                 for(var i = 0, len = sn.length; i < len; i++){
36285                     if(sn[i] == node){
36286                         index = i;
36287                         break;
36288                     }
36289                 }
36290             }
36291             if(index != -1){
36292                 this.selNodes.splice(index, 1);
36293             }
36294             delete this.selMap[node.id];
36295             this.fireEvent("selectionchange", this, this.selNodes);
36296         }
36297     },
36298     
36299     /**
36300      * Clear all selections
36301      */
36302     clearSelections : function(suppressEvent){
36303         var sn = this.selNodes;
36304         if(sn.length > 0){
36305             for(var i = 0, len = sn.length; i < len; i++){
36306                 sn[i].ui.onSelectedChange(false);
36307             }
36308             this.selNodes = [];
36309             this.selMap = {};
36310             if(suppressEvent !== true){
36311                 this.fireEvent("selectionchange", this, this.selNodes);
36312             }
36313         }
36314     },
36315     
36316     /**
36317      * Returns true if the node is selected
36318      * @param {TreeNode} node The node to check
36319      * @return {Boolean}
36320      */
36321     isSelected : function(node){
36322         return this.selMap[node.id] ? true : false;  
36323     },
36324     
36325     /**
36326      * Returns an array of the selected nodes
36327      * @return {Array}
36328      */
36329     getSelectedNodes : function(){
36330         return this.selNodes;    
36331     },
36332
36333     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
36334
36335     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
36336
36337     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
36338 });/*
36339  * Based on:
36340  * Ext JS Library 1.1.1
36341  * Copyright(c) 2006-2007, Ext JS, LLC.
36342  *
36343  * Originally Released Under LGPL - original licence link has changed is not relivant.
36344  *
36345  * Fork - LGPL
36346  * <script type="text/javascript">
36347  */
36348  
36349 /**
36350  * @class Roo.tree.TreeNode
36351  * @extends Roo.data.Node
36352  * @cfg {String} text The text for this node
36353  * @cfg {Boolean} expanded true to start the node expanded
36354  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36355  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36356  * @cfg {Boolean} disabled true to start the node disabled
36357  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36358  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36359  * @cfg {String} cls A css class to be added to the node
36360  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36361  * @cfg {String} href URL of the link used for the node (defaults to #)
36362  * @cfg {String} hrefTarget target frame for the link
36363  * @cfg {String} qtip An Ext QuickTip for the node
36364  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36365  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36366  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36367  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36368  * (defaults to undefined with no checkbox rendered)
36369  * @constructor
36370  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36371  */
36372 Roo.tree.TreeNode = function(attributes){
36373     attributes = attributes || {};
36374     if(typeof attributes == "string"){
36375         attributes = {text: attributes};
36376     }
36377     this.childrenRendered = false;
36378     this.rendered = false;
36379     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36380     this.expanded = attributes.expanded === true;
36381     this.isTarget = attributes.isTarget !== false;
36382     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36383     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36384
36385     /**
36386      * Read-only. The text for this node. To change it use setText().
36387      * @type String
36388      */
36389     this.text = attributes.text;
36390     /**
36391      * True if this node is disabled.
36392      * @type Boolean
36393      */
36394     this.disabled = attributes.disabled === true;
36395
36396     this.addEvents({
36397         /**
36398         * @event textchange
36399         * Fires when the text for this node is changed
36400         * @param {Node} this This node
36401         * @param {String} text The new text
36402         * @param {String} oldText The old text
36403         */
36404         "textchange" : true,
36405         /**
36406         * @event beforeexpand
36407         * Fires before this node is expanded, return false to cancel.
36408         * @param {Node} this This node
36409         * @param {Boolean} deep
36410         * @param {Boolean} anim
36411         */
36412         "beforeexpand" : true,
36413         /**
36414         * @event beforecollapse
36415         * Fires before this node is collapsed, return false to cancel.
36416         * @param {Node} this This node
36417         * @param {Boolean} deep
36418         * @param {Boolean} anim
36419         */
36420         "beforecollapse" : true,
36421         /**
36422         * @event expand
36423         * Fires when this node is expanded
36424         * @param {Node} this This node
36425         */
36426         "expand" : true,
36427         /**
36428         * @event disabledchange
36429         * Fires when the disabled status of this node changes
36430         * @param {Node} this This node
36431         * @param {Boolean} disabled
36432         */
36433         "disabledchange" : true,
36434         /**
36435         * @event collapse
36436         * Fires when this node is collapsed
36437         * @param {Node} this This node
36438         */
36439         "collapse" : true,
36440         /**
36441         * @event beforeclick
36442         * Fires before click processing. Return false to cancel the default action.
36443         * @param {Node} this This node
36444         * @param {Roo.EventObject} e The event object
36445         */
36446         "beforeclick":true,
36447         /**
36448         * @event checkchange
36449         * Fires when a node with a checkbox's checked property changes
36450         * @param {Node} this This node
36451         * @param {Boolean} checked
36452         */
36453         "checkchange":true,
36454         /**
36455         * @event click
36456         * Fires when this node is clicked
36457         * @param {Node} this This node
36458         * @param {Roo.EventObject} e The event object
36459         */
36460         "click":true,
36461         /**
36462         * @event dblclick
36463         * Fires when this node is double clicked
36464         * @param {Node} this This node
36465         * @param {Roo.EventObject} e The event object
36466         */
36467         "dblclick":true,
36468         /**
36469         * @event contextmenu
36470         * Fires when this node is right clicked
36471         * @param {Node} this This node
36472         * @param {Roo.EventObject} e The event object
36473         */
36474         "contextmenu":true,
36475         /**
36476         * @event beforechildrenrendered
36477         * Fires right before the child nodes for this node are rendered
36478         * @param {Node} this This node
36479         */
36480         "beforechildrenrendered":true
36481     });
36482
36483     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36484
36485     /**
36486      * Read-only. The UI for this node
36487      * @type TreeNodeUI
36488      */
36489     this.ui = new uiClass(this);
36490     
36491     // finally support items[]
36492     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36493         return;
36494     }
36495     
36496     
36497     Roo.each(this.attributes.items, function(c) {
36498         this.appendChild(Roo.factory(c,Roo.Tree));
36499     }, this);
36500     delete this.attributes.items;
36501     
36502     
36503     
36504 };
36505 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36506     preventHScroll: true,
36507     /**
36508      * Returns true if this node is expanded
36509      * @return {Boolean}
36510      */
36511     isExpanded : function(){
36512         return this.expanded;
36513     },
36514
36515     /**
36516      * Returns the UI object for this node
36517      * @return {TreeNodeUI}
36518      */
36519     getUI : function(){
36520         return this.ui;
36521     },
36522
36523     // private override
36524     setFirstChild : function(node){
36525         var of = this.firstChild;
36526         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36527         if(this.childrenRendered && of && node != of){
36528             of.renderIndent(true, true);
36529         }
36530         if(this.rendered){
36531             this.renderIndent(true, true);
36532         }
36533     },
36534
36535     // private override
36536     setLastChild : function(node){
36537         var ol = this.lastChild;
36538         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36539         if(this.childrenRendered && ol && node != ol){
36540             ol.renderIndent(true, true);
36541         }
36542         if(this.rendered){
36543             this.renderIndent(true, true);
36544         }
36545     },
36546
36547     // these methods are overridden to provide lazy rendering support
36548     // private override
36549     appendChild : function()
36550     {
36551         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36552         if(node && this.childrenRendered){
36553             node.render();
36554         }
36555         this.ui.updateExpandIcon();
36556         return node;
36557     },
36558
36559     // private override
36560     removeChild : function(node){
36561         this.ownerTree.getSelectionModel().unselect(node);
36562         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36563         // if it's been rendered remove dom node
36564         if(this.childrenRendered){
36565             node.ui.remove();
36566         }
36567         if(this.childNodes.length < 1){
36568             this.collapse(false, false);
36569         }else{
36570             this.ui.updateExpandIcon();
36571         }
36572         if(!this.firstChild) {
36573             this.childrenRendered = false;
36574         }
36575         return node;
36576     },
36577
36578     // private override
36579     insertBefore : function(node, refNode){
36580         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36581         if(newNode && refNode && this.childrenRendered){
36582             node.render();
36583         }
36584         this.ui.updateExpandIcon();
36585         return newNode;
36586     },
36587
36588     /**
36589      * Sets the text for this node
36590      * @param {String} text
36591      */
36592     setText : function(text){
36593         var oldText = this.text;
36594         this.text = text;
36595         this.attributes.text = text;
36596         if(this.rendered){ // event without subscribing
36597             this.ui.onTextChange(this, text, oldText);
36598         }
36599         this.fireEvent("textchange", this, text, oldText);
36600     },
36601
36602     /**
36603      * Triggers selection of this node
36604      */
36605     select : function(){
36606         this.getOwnerTree().getSelectionModel().select(this);
36607     },
36608
36609     /**
36610      * Triggers deselection of this node
36611      */
36612     unselect : function(){
36613         this.getOwnerTree().getSelectionModel().unselect(this);
36614     },
36615
36616     /**
36617      * Returns true if this node is selected
36618      * @return {Boolean}
36619      */
36620     isSelected : function(){
36621         return this.getOwnerTree().getSelectionModel().isSelected(this);
36622     },
36623
36624     /**
36625      * Expand this node.
36626      * @param {Boolean} deep (optional) True to expand all children as well
36627      * @param {Boolean} anim (optional) false to cancel the default animation
36628      * @param {Function} callback (optional) A callback to be called when
36629      * expanding this node completes (does not wait for deep expand to complete).
36630      * Called with 1 parameter, this node.
36631      */
36632     expand : function(deep, anim, callback){
36633         if(!this.expanded){
36634             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36635                 return;
36636             }
36637             if(!this.childrenRendered){
36638                 this.renderChildren();
36639             }
36640             this.expanded = true;
36641             
36642             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36643                 this.ui.animExpand(function(){
36644                     this.fireEvent("expand", this);
36645                     if(typeof callback == "function"){
36646                         callback(this);
36647                     }
36648                     if(deep === true){
36649                         this.expandChildNodes(true);
36650                     }
36651                 }.createDelegate(this));
36652                 return;
36653             }else{
36654                 this.ui.expand();
36655                 this.fireEvent("expand", this);
36656                 if(typeof callback == "function"){
36657                     callback(this);
36658                 }
36659             }
36660         }else{
36661            if(typeof callback == "function"){
36662                callback(this);
36663            }
36664         }
36665         if(deep === true){
36666             this.expandChildNodes(true);
36667         }
36668     },
36669
36670     isHiddenRoot : function(){
36671         return this.isRoot && !this.getOwnerTree().rootVisible;
36672     },
36673
36674     /**
36675      * Collapse this node.
36676      * @param {Boolean} deep (optional) True to collapse all children as well
36677      * @param {Boolean} anim (optional) false to cancel the default animation
36678      */
36679     collapse : function(deep, anim){
36680         if(this.expanded && !this.isHiddenRoot()){
36681             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36682                 return;
36683             }
36684             this.expanded = false;
36685             if((this.getOwnerTree().animate && anim !== false) || anim){
36686                 this.ui.animCollapse(function(){
36687                     this.fireEvent("collapse", this);
36688                     if(deep === true){
36689                         this.collapseChildNodes(true);
36690                     }
36691                 }.createDelegate(this));
36692                 return;
36693             }else{
36694                 this.ui.collapse();
36695                 this.fireEvent("collapse", this);
36696             }
36697         }
36698         if(deep === true){
36699             var cs = this.childNodes;
36700             for(var i = 0, len = cs.length; i < len; i++) {
36701                 cs[i].collapse(true, false);
36702             }
36703         }
36704     },
36705
36706     // private
36707     delayedExpand : function(delay){
36708         if(!this.expandProcId){
36709             this.expandProcId = this.expand.defer(delay, this);
36710         }
36711     },
36712
36713     // private
36714     cancelExpand : function(){
36715         if(this.expandProcId){
36716             clearTimeout(this.expandProcId);
36717         }
36718         this.expandProcId = false;
36719     },
36720
36721     /**
36722      * Toggles expanded/collapsed state of the node
36723      */
36724     toggle : function(){
36725         if(this.expanded){
36726             this.collapse();
36727         }else{
36728             this.expand();
36729         }
36730     },
36731
36732     /**
36733      * Ensures all parent nodes are expanded
36734      */
36735     ensureVisible : function(callback){
36736         var tree = this.getOwnerTree();
36737         tree.expandPath(this.parentNode.getPath(), false, function(){
36738             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36739             Roo.callback(callback);
36740         }.createDelegate(this));
36741     },
36742
36743     /**
36744      * Expand all child nodes
36745      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36746      */
36747     expandChildNodes : function(deep){
36748         var cs = this.childNodes;
36749         for(var i = 0, len = cs.length; i < len; i++) {
36750                 cs[i].expand(deep);
36751         }
36752     },
36753
36754     /**
36755      * Collapse all child nodes
36756      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36757      */
36758     collapseChildNodes : function(deep){
36759         var cs = this.childNodes;
36760         for(var i = 0, len = cs.length; i < len; i++) {
36761                 cs[i].collapse(deep);
36762         }
36763     },
36764
36765     /**
36766      * Disables this node
36767      */
36768     disable : function(){
36769         this.disabled = true;
36770         this.unselect();
36771         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36772             this.ui.onDisableChange(this, true);
36773         }
36774         this.fireEvent("disabledchange", this, true);
36775     },
36776
36777     /**
36778      * Enables this node
36779      */
36780     enable : function(){
36781         this.disabled = false;
36782         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36783             this.ui.onDisableChange(this, false);
36784         }
36785         this.fireEvent("disabledchange", this, false);
36786     },
36787
36788     // private
36789     renderChildren : function(suppressEvent){
36790         if(suppressEvent !== false){
36791             this.fireEvent("beforechildrenrendered", this);
36792         }
36793         var cs = this.childNodes;
36794         for(var i = 0, len = cs.length; i < len; i++){
36795             cs[i].render(true);
36796         }
36797         this.childrenRendered = true;
36798     },
36799
36800     // private
36801     sort : function(fn, scope){
36802         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36803         if(this.childrenRendered){
36804             var cs = this.childNodes;
36805             for(var i = 0, len = cs.length; i < len; i++){
36806                 cs[i].render(true);
36807             }
36808         }
36809     },
36810
36811     // private
36812     render : function(bulkRender){
36813         this.ui.render(bulkRender);
36814         if(!this.rendered){
36815             this.rendered = true;
36816             if(this.expanded){
36817                 this.expanded = false;
36818                 this.expand(false, false);
36819             }
36820         }
36821     },
36822
36823     // private
36824     renderIndent : function(deep, refresh){
36825         if(refresh){
36826             this.ui.childIndent = null;
36827         }
36828         this.ui.renderIndent();
36829         if(deep === true && this.childrenRendered){
36830             var cs = this.childNodes;
36831             for(var i = 0, len = cs.length; i < len; i++){
36832                 cs[i].renderIndent(true, refresh);
36833             }
36834         }
36835     }
36836 });/*
36837  * Based on:
36838  * Ext JS Library 1.1.1
36839  * Copyright(c) 2006-2007, Ext JS, LLC.
36840  *
36841  * Originally Released Under LGPL - original licence link has changed is not relivant.
36842  *
36843  * Fork - LGPL
36844  * <script type="text/javascript">
36845  */
36846  
36847 /**
36848  * @class Roo.tree.AsyncTreeNode
36849  * @extends Roo.tree.TreeNode
36850  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36851  * @constructor
36852  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36853  */
36854  Roo.tree.AsyncTreeNode = function(config){
36855     this.loaded = false;
36856     this.loading = false;
36857     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36858     /**
36859     * @event beforeload
36860     * Fires before this node is loaded, return false to cancel
36861     * @param {Node} this This node
36862     */
36863     this.addEvents({'beforeload':true, 'load': true});
36864     /**
36865     * @event load
36866     * Fires when this node is loaded
36867     * @param {Node} this This node
36868     */
36869     /**
36870      * The loader used by this node (defaults to using the tree's defined loader)
36871      * @type TreeLoader
36872      * @property loader
36873      */
36874 };
36875 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36876     expand : function(deep, anim, callback){
36877         if(this.loading){ // if an async load is already running, waiting til it's done
36878             var timer;
36879             var f = function(){
36880                 if(!this.loading){ // done loading
36881                     clearInterval(timer);
36882                     this.expand(deep, anim, callback);
36883                 }
36884             }.createDelegate(this);
36885             timer = setInterval(f, 200);
36886             return;
36887         }
36888         if(!this.loaded){
36889             if(this.fireEvent("beforeload", this) === false){
36890                 return;
36891             }
36892             this.loading = true;
36893             this.ui.beforeLoad(this);
36894             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36895             if(loader){
36896                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36897                 return;
36898             }
36899         }
36900         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36901     },
36902     
36903     /**
36904      * Returns true if this node is currently loading
36905      * @return {Boolean}
36906      */
36907     isLoading : function(){
36908         return this.loading;  
36909     },
36910     
36911     loadComplete : function(deep, anim, callback){
36912         this.loading = false;
36913         this.loaded = true;
36914         this.ui.afterLoad(this);
36915         this.fireEvent("load", this);
36916         this.expand(deep, anim, callback);
36917     },
36918     
36919     /**
36920      * Returns true if this node has been loaded
36921      * @return {Boolean}
36922      */
36923     isLoaded : function(){
36924         return this.loaded;
36925     },
36926     
36927     hasChildNodes : function(){
36928         if(!this.isLeaf() && !this.loaded){
36929             return true;
36930         }else{
36931             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36932         }
36933     },
36934
36935     /**
36936      * Trigger a reload for this node
36937      * @param {Function} callback
36938      */
36939     reload : function(callback){
36940         this.collapse(false, false);
36941         while(this.firstChild){
36942             this.removeChild(this.firstChild);
36943         }
36944         this.childrenRendered = false;
36945         this.loaded = false;
36946         if(this.isHiddenRoot()){
36947             this.expanded = false;
36948         }
36949         this.expand(false, false, callback);
36950     }
36951 });/*
36952  * Based on:
36953  * Ext JS Library 1.1.1
36954  * Copyright(c) 2006-2007, Ext JS, LLC.
36955  *
36956  * Originally Released Under LGPL - original licence link has changed is not relivant.
36957  *
36958  * Fork - LGPL
36959  * <script type="text/javascript">
36960  */
36961  
36962 /**
36963  * @class Roo.tree.TreeNodeUI
36964  * @constructor
36965  * @param {Object} node The node to render
36966  * The TreeNode UI implementation is separate from the
36967  * tree implementation. Unless you are customizing the tree UI,
36968  * you should never have to use this directly.
36969  */
36970 Roo.tree.TreeNodeUI = function(node){
36971     this.node = node;
36972     this.rendered = false;
36973     this.animating = false;
36974     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36975 };
36976
36977 Roo.tree.TreeNodeUI.prototype = {
36978     removeChild : function(node){
36979         if(this.rendered){
36980             this.ctNode.removeChild(node.ui.getEl());
36981         }
36982     },
36983
36984     beforeLoad : function(){
36985          this.addClass("x-tree-node-loading");
36986     },
36987
36988     afterLoad : function(){
36989          this.removeClass("x-tree-node-loading");
36990     },
36991
36992     onTextChange : function(node, text, oldText){
36993         if(this.rendered){
36994             this.textNode.innerHTML = text;
36995         }
36996     },
36997
36998     onDisableChange : function(node, state){
36999         this.disabled = state;
37000         if(state){
37001             this.addClass("x-tree-node-disabled");
37002         }else{
37003             this.removeClass("x-tree-node-disabled");
37004         }
37005     },
37006
37007     onSelectedChange : function(state){
37008         if(state){
37009             this.focus();
37010             this.addClass("x-tree-selected");
37011         }else{
37012             //this.blur();
37013             this.removeClass("x-tree-selected");
37014         }
37015     },
37016
37017     onMove : function(tree, node, oldParent, newParent, index, refNode){
37018         this.childIndent = null;
37019         if(this.rendered){
37020             var targetNode = newParent.ui.getContainer();
37021             if(!targetNode){//target not rendered
37022                 this.holder = document.createElement("div");
37023                 this.holder.appendChild(this.wrap);
37024                 return;
37025             }
37026             var insertBefore = refNode ? refNode.ui.getEl() : null;
37027             if(insertBefore){
37028                 targetNode.insertBefore(this.wrap, insertBefore);
37029             }else{
37030                 targetNode.appendChild(this.wrap);
37031             }
37032             this.node.renderIndent(true);
37033         }
37034     },
37035
37036     addClass : function(cls){
37037         if(this.elNode){
37038             Roo.fly(this.elNode).addClass(cls);
37039         }
37040     },
37041
37042     removeClass : function(cls){
37043         if(this.elNode){
37044             Roo.fly(this.elNode).removeClass(cls);
37045         }
37046     },
37047
37048     remove : function(){
37049         if(this.rendered){
37050             this.holder = document.createElement("div");
37051             this.holder.appendChild(this.wrap);
37052         }
37053     },
37054
37055     fireEvent : function(){
37056         return this.node.fireEvent.apply(this.node, arguments);
37057     },
37058
37059     initEvents : function(){
37060         this.node.on("move", this.onMove, this);
37061         var E = Roo.EventManager;
37062         var a = this.anchor;
37063
37064         var el = Roo.fly(a, '_treeui');
37065
37066         if(Roo.isOpera){ // opera render bug ignores the CSS
37067             el.setStyle("text-decoration", "none");
37068         }
37069
37070         el.on("click", this.onClick, this);
37071         el.on("dblclick", this.onDblClick, this);
37072
37073         if(this.checkbox){
37074             Roo.EventManager.on(this.checkbox,
37075                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
37076         }
37077
37078         el.on("contextmenu", this.onContextMenu, this);
37079
37080         var icon = Roo.fly(this.iconNode);
37081         icon.on("click", this.onClick, this);
37082         icon.on("dblclick", this.onDblClick, this);
37083         icon.on("contextmenu", this.onContextMenu, this);
37084         E.on(this.ecNode, "click", this.ecClick, this, true);
37085
37086         if(this.node.disabled){
37087             this.addClass("x-tree-node-disabled");
37088         }
37089         if(this.node.hidden){
37090             this.addClass("x-tree-node-disabled");
37091         }
37092         var ot = this.node.getOwnerTree();
37093         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
37094         if(dd && (!this.node.isRoot || ot.rootVisible)){
37095             Roo.dd.Registry.register(this.elNode, {
37096                 node: this.node,
37097                 handles: this.getDDHandles(),
37098                 isHandle: false
37099             });
37100         }
37101     },
37102
37103     getDDHandles : function(){
37104         return [this.iconNode, this.textNode];
37105     },
37106
37107     hide : function(){
37108         if(this.rendered){
37109             this.wrap.style.display = "none";
37110         }
37111     },
37112
37113     show : function(){
37114         if(this.rendered){
37115             this.wrap.style.display = "";
37116         }
37117     },
37118
37119     onContextMenu : function(e){
37120         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
37121             e.preventDefault();
37122             this.focus();
37123             this.fireEvent("contextmenu", this.node, e);
37124         }
37125     },
37126
37127     onClick : function(e){
37128         if(this.dropping){
37129             e.stopEvent();
37130             return;
37131         }
37132         if(this.fireEvent("beforeclick", this.node, e) !== false){
37133             if(!this.disabled && this.node.attributes.href){
37134                 this.fireEvent("click", this.node, e);
37135                 return;
37136             }
37137             e.preventDefault();
37138             if(this.disabled){
37139                 return;
37140             }
37141
37142             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
37143                 this.node.toggle();
37144             }
37145
37146             this.fireEvent("click", this.node, e);
37147         }else{
37148             e.stopEvent();
37149         }
37150     },
37151
37152     onDblClick : function(e){
37153         e.preventDefault();
37154         if(this.disabled){
37155             return;
37156         }
37157         if(this.checkbox){
37158             this.toggleCheck();
37159         }
37160         if(!this.animating && this.node.hasChildNodes()){
37161             this.node.toggle();
37162         }
37163         this.fireEvent("dblclick", this.node, e);
37164     },
37165
37166     onCheckChange : function(){
37167         var checked = this.checkbox.checked;
37168         this.node.attributes.checked = checked;
37169         this.fireEvent('checkchange', this.node, checked);
37170     },
37171
37172     ecClick : function(e){
37173         if(!this.animating && this.node.hasChildNodes()){
37174             this.node.toggle();
37175         }
37176     },
37177
37178     startDrop : function(){
37179         this.dropping = true;
37180     },
37181
37182     // delayed drop so the click event doesn't get fired on a drop
37183     endDrop : function(){
37184        setTimeout(function(){
37185            this.dropping = false;
37186        }.createDelegate(this), 50);
37187     },
37188
37189     expand : function(){
37190         this.updateExpandIcon();
37191         this.ctNode.style.display = "";
37192     },
37193
37194     focus : function(){
37195         if(!this.node.preventHScroll){
37196             try{this.anchor.focus();
37197             }catch(e){}
37198         }else if(!Roo.isIE){
37199             try{
37200                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
37201                 var l = noscroll.scrollLeft;
37202                 this.anchor.focus();
37203                 noscroll.scrollLeft = l;
37204             }catch(e){}
37205         }
37206     },
37207
37208     toggleCheck : function(value){
37209         var cb = this.checkbox;
37210         if(cb){
37211             cb.checked = (value === undefined ? !cb.checked : value);
37212         }
37213     },
37214
37215     blur : function(){
37216         try{
37217             this.anchor.blur();
37218         }catch(e){}
37219     },
37220
37221     animExpand : function(callback){
37222         var ct = Roo.get(this.ctNode);
37223         ct.stopFx();
37224         if(!this.node.hasChildNodes()){
37225             this.updateExpandIcon();
37226             this.ctNode.style.display = "";
37227             Roo.callback(callback);
37228             return;
37229         }
37230         this.animating = true;
37231         this.updateExpandIcon();
37232
37233         ct.slideIn('t', {
37234            callback : function(){
37235                this.animating = false;
37236                Roo.callback(callback);
37237             },
37238             scope: this,
37239             duration: this.node.ownerTree.duration || .25
37240         });
37241     },
37242
37243     highlight : function(){
37244         var tree = this.node.getOwnerTree();
37245         Roo.fly(this.wrap).highlight(
37246             tree.hlColor || "C3DAF9",
37247             {endColor: tree.hlBaseColor}
37248         );
37249     },
37250
37251     collapse : function(){
37252         this.updateExpandIcon();
37253         this.ctNode.style.display = "none";
37254     },
37255
37256     animCollapse : function(callback){
37257         var ct = Roo.get(this.ctNode);
37258         ct.enableDisplayMode('block');
37259         ct.stopFx();
37260
37261         this.animating = true;
37262         this.updateExpandIcon();
37263
37264         ct.slideOut('t', {
37265             callback : function(){
37266                this.animating = false;
37267                Roo.callback(callback);
37268             },
37269             scope: this,
37270             duration: this.node.ownerTree.duration || .25
37271         });
37272     },
37273
37274     getContainer : function(){
37275         return this.ctNode;
37276     },
37277
37278     getEl : function(){
37279         return this.wrap;
37280     },
37281
37282     appendDDGhost : function(ghostNode){
37283         ghostNode.appendChild(this.elNode.cloneNode(true));
37284     },
37285
37286     getDDRepairXY : function(){
37287         return Roo.lib.Dom.getXY(this.iconNode);
37288     },
37289
37290     onRender : function(){
37291         this.render();
37292     },
37293
37294     render : function(bulkRender){
37295         var n = this.node, a = n.attributes;
37296         var targetNode = n.parentNode ?
37297               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
37298
37299         if(!this.rendered){
37300             this.rendered = true;
37301
37302             this.renderElements(n, a, targetNode, bulkRender);
37303
37304             if(a.qtip){
37305                if(this.textNode.setAttributeNS){
37306                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
37307                    if(a.qtipTitle){
37308                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
37309                    }
37310                }else{
37311                    this.textNode.setAttribute("ext:qtip", a.qtip);
37312                    if(a.qtipTitle){
37313                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
37314                    }
37315                }
37316             }else if(a.qtipCfg){
37317                 a.qtipCfg.target = Roo.id(this.textNode);
37318                 Roo.QuickTips.register(a.qtipCfg);
37319             }
37320             this.initEvents();
37321             if(!this.node.expanded){
37322                 this.updateExpandIcon();
37323             }
37324         }else{
37325             if(bulkRender === true) {
37326                 targetNode.appendChild(this.wrap);
37327             }
37328         }
37329     },
37330
37331     renderElements : function(n, a, targetNode, bulkRender)
37332     {
37333         // add some indent caching, this helps performance when rendering a large tree
37334         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37335         var t = n.getOwnerTree();
37336         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
37337         if (typeof(n.attributes.html) != 'undefined') {
37338             txt = n.attributes.html;
37339         }
37340         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
37341         var cb = typeof a.checked == 'boolean';
37342         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37343         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
37344             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
37345             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
37346             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37347             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37348             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37349              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37350                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37351             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37352             "</li>"];
37353
37354         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37355             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37356                                 n.nextSibling.ui.getEl(), buf.join(""));
37357         }else{
37358             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37359         }
37360
37361         this.elNode = this.wrap.childNodes[0];
37362         this.ctNode = this.wrap.childNodes[1];
37363         var cs = this.elNode.childNodes;
37364         this.indentNode = cs[0];
37365         this.ecNode = cs[1];
37366         this.iconNode = cs[2];
37367         var index = 3;
37368         if(cb){
37369             this.checkbox = cs[3];
37370             index++;
37371         }
37372         this.anchor = cs[index];
37373         this.textNode = cs[index].firstChild;
37374     },
37375
37376     getAnchor : function(){
37377         return this.anchor;
37378     },
37379
37380     getTextEl : function(){
37381         return this.textNode;
37382     },
37383
37384     getIconEl : function(){
37385         return this.iconNode;
37386     },
37387
37388     isChecked : function(){
37389         return this.checkbox ? this.checkbox.checked : false;
37390     },
37391
37392     updateExpandIcon : function(){
37393         if(this.rendered){
37394             var n = this.node, c1, c2;
37395             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37396             var hasChild = n.hasChildNodes();
37397             if(hasChild){
37398                 if(n.expanded){
37399                     cls += "-minus";
37400                     c1 = "x-tree-node-collapsed";
37401                     c2 = "x-tree-node-expanded";
37402                 }else{
37403                     cls += "-plus";
37404                     c1 = "x-tree-node-expanded";
37405                     c2 = "x-tree-node-collapsed";
37406                 }
37407                 if(this.wasLeaf){
37408                     this.removeClass("x-tree-node-leaf");
37409                     this.wasLeaf = false;
37410                 }
37411                 if(this.c1 != c1 || this.c2 != c2){
37412                     Roo.fly(this.elNode).replaceClass(c1, c2);
37413                     this.c1 = c1; this.c2 = c2;
37414                 }
37415             }else{
37416                 // this changes non-leafs into leafs if they have no children.
37417                 // it's not very rational behaviour..
37418                 
37419                 if(!this.wasLeaf && this.node.leaf){
37420                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37421                     delete this.c1;
37422                     delete this.c2;
37423                     this.wasLeaf = true;
37424                 }
37425             }
37426             var ecc = "x-tree-ec-icon "+cls;
37427             if(this.ecc != ecc){
37428                 this.ecNode.className = ecc;
37429                 this.ecc = ecc;
37430             }
37431         }
37432     },
37433
37434     getChildIndent : function(){
37435         if(!this.childIndent){
37436             var buf = [];
37437             var p = this.node;
37438             while(p){
37439                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37440                     if(!p.isLast()) {
37441                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37442                     } else {
37443                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37444                     }
37445                 }
37446                 p = p.parentNode;
37447             }
37448             this.childIndent = buf.join("");
37449         }
37450         return this.childIndent;
37451     },
37452
37453     renderIndent : function(){
37454         if(this.rendered){
37455             var indent = "";
37456             var p = this.node.parentNode;
37457             if(p){
37458                 indent = p.ui.getChildIndent();
37459             }
37460             if(this.indentMarkup != indent){ // don't rerender if not required
37461                 this.indentNode.innerHTML = indent;
37462                 this.indentMarkup = indent;
37463             }
37464             this.updateExpandIcon();
37465         }
37466     }
37467 };
37468
37469 Roo.tree.RootTreeNodeUI = function(){
37470     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37471 };
37472 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37473     render : function(){
37474         if(!this.rendered){
37475             var targetNode = this.node.ownerTree.innerCt.dom;
37476             this.node.expanded = true;
37477             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37478             this.wrap = this.ctNode = targetNode.firstChild;
37479         }
37480     },
37481     collapse : function(){
37482     },
37483     expand : function(){
37484     }
37485 });/*
37486  * Based on:
37487  * Ext JS Library 1.1.1
37488  * Copyright(c) 2006-2007, Ext JS, LLC.
37489  *
37490  * Originally Released Under LGPL - original licence link has changed is not relivant.
37491  *
37492  * Fork - LGPL
37493  * <script type="text/javascript">
37494  */
37495 /**
37496  * @class Roo.tree.TreeLoader
37497  * @extends Roo.util.Observable
37498  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37499  * nodes from a specified URL. The response must be a javascript Array definition
37500  * who's elements are node definition objects. eg:
37501  * <pre><code>
37502 {  success : true,
37503    data :      [
37504    
37505     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37506     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37507     ]
37508 }
37509
37510
37511 </code></pre>
37512  * <br><br>
37513  * The old style respose with just an array is still supported, but not recommended.
37514  * <br><br>
37515  *
37516  * A server request is sent, and child nodes are loaded only when a node is expanded.
37517  * The loading node's id is passed to the server under the parameter name "node" to
37518  * enable the server to produce the correct child nodes.
37519  * <br><br>
37520  * To pass extra parameters, an event handler may be attached to the "beforeload"
37521  * event, and the parameters specified in the TreeLoader's baseParams property:
37522  * <pre><code>
37523     myTreeLoader.on("beforeload", function(treeLoader, node) {
37524         this.baseParams.category = node.attributes.category;
37525     }, this);
37526     
37527 </code></pre>
37528  *
37529  * This would pass an HTTP parameter called "category" to the server containing
37530  * the value of the Node's "category" attribute.
37531  * @constructor
37532  * Creates a new Treeloader.
37533  * @param {Object} config A config object containing config properties.
37534  */
37535 Roo.tree.TreeLoader = function(config){
37536     this.baseParams = {};
37537     this.requestMethod = "POST";
37538     Roo.apply(this, config);
37539
37540     this.addEvents({
37541     
37542         /**
37543          * @event beforeload
37544          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37545          * @param {Object} This TreeLoader object.
37546          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37547          * @param {Object} callback The callback function specified in the {@link #load} call.
37548          */
37549         beforeload : true,
37550         /**
37551          * @event load
37552          * Fires when the node has been successfuly loaded.
37553          * @param {Object} This TreeLoader object.
37554          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37555          * @param {Object} response The response object containing the data from the server.
37556          */
37557         load : true,
37558         /**
37559          * @event loadexception
37560          * Fires if the network request failed.
37561          * @param {Object} This TreeLoader object.
37562          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37563          * @param {Object} response The response object containing the data from the server.
37564          */
37565         loadexception : true,
37566         /**
37567          * @event create
37568          * Fires before a node is created, enabling you to return custom Node types 
37569          * @param {Object} This TreeLoader object.
37570          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37571          */
37572         create : true
37573     });
37574
37575     Roo.tree.TreeLoader.superclass.constructor.call(this);
37576 };
37577
37578 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37579     /**
37580     * @cfg {String} dataUrl The URL from which to request a Json string which
37581     * specifies an array of node definition object representing the child nodes
37582     * to be loaded.
37583     */
37584     /**
37585     * @cfg {String} requestMethod either GET or POST
37586     * defaults to POST (due to BC)
37587     * to be loaded.
37588     */
37589     /**
37590     * @cfg {Object} baseParams (optional) An object containing properties which
37591     * specify HTTP parameters to be passed to each request for child nodes.
37592     */
37593     /**
37594     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37595     * created by this loader. If the attributes sent by the server have an attribute in this object,
37596     * they take priority.
37597     */
37598     /**
37599     * @cfg {Object} uiProviders (optional) An object containing properties which
37600     * 
37601     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37602     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37603     * <i>uiProvider</i> attribute of a returned child node is a string rather
37604     * than a reference to a TreeNodeUI implementation, this that string value
37605     * is used as a property name in the uiProviders object. You can define the provider named
37606     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37607     */
37608     uiProviders : {},
37609
37610     /**
37611     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37612     * child nodes before loading.
37613     */
37614     clearOnLoad : true,
37615
37616     /**
37617     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37618     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37619     * Grid query { data : [ .....] }
37620     */
37621     
37622     root : false,
37623      /**
37624     * @cfg {String} queryParam (optional) 
37625     * Name of the query as it will be passed on the querystring (defaults to 'node')
37626     * eg. the request will be ?node=[id]
37627     */
37628     
37629     
37630     queryParam: false,
37631     
37632     /**
37633      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37634      * This is called automatically when a node is expanded, but may be used to reload
37635      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37636      * @param {Roo.tree.TreeNode} node
37637      * @param {Function} callback
37638      */
37639     load : function(node, callback){
37640         if(this.clearOnLoad){
37641             while(node.firstChild){
37642                 node.removeChild(node.firstChild);
37643             }
37644         }
37645         if(node.attributes.children){ // preloaded json children
37646             var cs = node.attributes.children;
37647             for(var i = 0, len = cs.length; i < len; i++){
37648                 node.appendChild(this.createNode(cs[i]));
37649             }
37650             if(typeof callback == "function"){
37651                 callback();
37652             }
37653         }else if(this.dataUrl){
37654             this.requestData(node, callback);
37655         }
37656     },
37657
37658     getParams: function(node){
37659         var buf = [], bp = this.baseParams;
37660         for(var key in bp){
37661             if(typeof bp[key] != "function"){
37662                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37663             }
37664         }
37665         var n = this.queryParam === false ? 'node' : this.queryParam;
37666         buf.push(n + "=", encodeURIComponent(node.id));
37667         return buf.join("");
37668     },
37669
37670     requestData : function(node, callback){
37671         if(this.fireEvent("beforeload", this, node, callback) !== false){
37672             this.transId = Roo.Ajax.request({
37673                 method:this.requestMethod,
37674                 url: this.dataUrl||this.url,
37675                 success: this.handleResponse,
37676                 failure: this.handleFailure,
37677                 scope: this,
37678                 argument: {callback: callback, node: node},
37679                 params: this.getParams(node)
37680             });
37681         }else{
37682             // if the load is cancelled, make sure we notify
37683             // the node that we are done
37684             if(typeof callback == "function"){
37685                 callback();
37686             }
37687         }
37688     },
37689
37690     isLoading : function(){
37691         return this.transId ? true : false;
37692     },
37693
37694     abort : function(){
37695         if(this.isLoading()){
37696             Roo.Ajax.abort(this.transId);
37697         }
37698     },
37699
37700     // private
37701     createNode : function(attr)
37702     {
37703         // apply baseAttrs, nice idea Corey!
37704         if(this.baseAttrs){
37705             Roo.applyIf(attr, this.baseAttrs);
37706         }
37707         if(this.applyLoader !== false){
37708             attr.loader = this;
37709         }
37710         // uiProvider = depreciated..
37711         
37712         if(typeof(attr.uiProvider) == 'string'){
37713            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37714                 /**  eval:var:attr */ eval(attr.uiProvider);
37715         }
37716         if(typeof(this.uiProviders['default']) != 'undefined') {
37717             attr.uiProvider = this.uiProviders['default'];
37718         }
37719         
37720         this.fireEvent('create', this, attr);
37721         
37722         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37723         return(attr.leaf ?
37724                         new Roo.tree.TreeNode(attr) :
37725                         new Roo.tree.AsyncTreeNode(attr));
37726     },
37727
37728     processResponse : function(response, node, callback)
37729     {
37730         var json = response.responseText;
37731         try {
37732             
37733             var o = Roo.decode(json);
37734             
37735             if (this.root === false && typeof(o.success) != undefined) {
37736                 this.root = 'data'; // the default behaviour for list like data..
37737                 }
37738                 
37739             if (this.root !== false &&  !o.success) {
37740                 // it's a failure condition.
37741                 var a = response.argument;
37742                 this.fireEvent("loadexception", this, a.node, response);
37743                 Roo.log("Load failed - should have a handler really");
37744                 return;
37745             }
37746             
37747             
37748             
37749             if (this.root !== false) {
37750                  o = o[this.root];
37751             }
37752             
37753             for(var i = 0, len = o.length; i < len; i++){
37754                 var n = this.createNode(o[i]);
37755                 if(n){
37756                     node.appendChild(n);
37757                 }
37758             }
37759             if(typeof callback == "function"){
37760                 callback(this, node);
37761             }
37762         }catch(e){
37763             this.handleFailure(response);
37764         }
37765     },
37766
37767     handleResponse : function(response){
37768         this.transId = false;
37769         var a = response.argument;
37770         this.processResponse(response, a.node, a.callback);
37771         this.fireEvent("load", this, a.node, response);
37772     },
37773
37774     handleFailure : function(response)
37775     {
37776         // should handle failure better..
37777         this.transId = false;
37778         var a = response.argument;
37779         this.fireEvent("loadexception", this, a.node, response);
37780         if(typeof a.callback == "function"){
37781             a.callback(this, a.node);
37782         }
37783     }
37784 });/*
37785  * Based on:
37786  * Ext JS Library 1.1.1
37787  * Copyright(c) 2006-2007, Ext JS, LLC.
37788  *
37789  * Originally Released Under LGPL - original licence link has changed is not relivant.
37790  *
37791  * Fork - LGPL
37792  * <script type="text/javascript">
37793  */
37794
37795 /**
37796 * @class Roo.tree.TreeFilter
37797 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37798 * @param {TreePanel} tree
37799 * @param {Object} config (optional)
37800  */
37801 Roo.tree.TreeFilter = function(tree, config){
37802     this.tree = tree;
37803     this.filtered = {};
37804     Roo.apply(this, config);
37805 };
37806
37807 Roo.tree.TreeFilter.prototype = {
37808     clearBlank:false,
37809     reverse:false,
37810     autoClear:false,
37811     remove:false,
37812
37813      /**
37814      * Filter the data by a specific attribute.
37815      * @param {String/RegExp} value Either string that the attribute value
37816      * should start with or a RegExp to test against the attribute
37817      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37818      * @param {TreeNode} startNode (optional) The node to start the filter at.
37819      */
37820     filter : function(value, attr, startNode){
37821         attr = attr || "text";
37822         var f;
37823         if(typeof value == "string"){
37824             var vlen = value.length;
37825             // auto clear empty filter
37826             if(vlen == 0 && this.clearBlank){
37827                 this.clear();
37828                 return;
37829             }
37830             value = value.toLowerCase();
37831             f = function(n){
37832                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37833             };
37834         }else if(value.exec){ // regex?
37835             f = function(n){
37836                 return value.test(n.attributes[attr]);
37837             };
37838         }else{
37839             throw 'Illegal filter type, must be string or regex';
37840         }
37841         this.filterBy(f, null, startNode);
37842         },
37843
37844     /**
37845      * Filter by a function. The passed function will be called with each
37846      * node in the tree (or from the startNode). If the function returns true, the node is kept
37847      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37848      * @param {Function} fn The filter function
37849      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37850      */
37851     filterBy : function(fn, scope, startNode){
37852         startNode = startNode || this.tree.root;
37853         if(this.autoClear){
37854             this.clear();
37855         }
37856         var af = this.filtered, rv = this.reverse;
37857         var f = function(n){
37858             if(n == startNode){
37859                 return true;
37860             }
37861             if(af[n.id]){
37862                 return false;
37863             }
37864             var m = fn.call(scope || n, n);
37865             if(!m || rv){
37866                 af[n.id] = n;
37867                 n.ui.hide();
37868                 return false;
37869             }
37870             return true;
37871         };
37872         startNode.cascade(f);
37873         if(this.remove){
37874            for(var id in af){
37875                if(typeof id != "function"){
37876                    var n = af[id];
37877                    if(n && n.parentNode){
37878                        n.parentNode.removeChild(n);
37879                    }
37880                }
37881            }
37882         }
37883     },
37884
37885     /**
37886      * Clears the current filter. Note: with the "remove" option
37887      * set a filter cannot be cleared.
37888      */
37889     clear : function(){
37890         var t = this.tree;
37891         var af = this.filtered;
37892         for(var id in af){
37893             if(typeof id != "function"){
37894                 var n = af[id];
37895                 if(n){
37896                     n.ui.show();
37897                 }
37898             }
37899         }
37900         this.filtered = {};
37901     }
37902 };
37903 /*
37904  * Based on:
37905  * Ext JS Library 1.1.1
37906  * Copyright(c) 2006-2007, Ext JS, LLC.
37907  *
37908  * Originally Released Under LGPL - original licence link has changed is not relivant.
37909  *
37910  * Fork - LGPL
37911  * <script type="text/javascript">
37912  */
37913  
37914
37915 /**
37916  * @class Roo.tree.TreeSorter
37917  * Provides sorting of nodes in a TreePanel
37918  * 
37919  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37920  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37921  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37922  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37923  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37924  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37925  * @constructor
37926  * @param {TreePanel} tree
37927  * @param {Object} config
37928  */
37929 Roo.tree.TreeSorter = function(tree, config){
37930     Roo.apply(this, config);
37931     tree.on("beforechildrenrendered", this.doSort, this);
37932     tree.on("append", this.updateSort, this);
37933     tree.on("insert", this.updateSort, this);
37934     
37935     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37936     var p = this.property || "text";
37937     var sortType = this.sortType;
37938     var fs = this.folderSort;
37939     var cs = this.caseSensitive === true;
37940     var leafAttr = this.leafAttr || 'leaf';
37941
37942     this.sortFn = function(n1, n2){
37943         if(fs){
37944             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37945                 return 1;
37946             }
37947             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37948                 return -1;
37949             }
37950         }
37951         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37952         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37953         if(v1 < v2){
37954                         return dsc ? +1 : -1;
37955                 }else if(v1 > v2){
37956                         return dsc ? -1 : +1;
37957         }else{
37958                 return 0;
37959         }
37960     };
37961 };
37962
37963 Roo.tree.TreeSorter.prototype = {
37964     doSort : function(node){
37965         node.sort(this.sortFn);
37966     },
37967     
37968     compareNodes : function(n1, n2){
37969         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37970     },
37971     
37972     updateSort : function(tree, node){
37973         if(node.childrenRendered){
37974             this.doSort.defer(1, this, [node]);
37975         }
37976     }
37977 };/*
37978  * Based on:
37979  * Ext JS Library 1.1.1
37980  * Copyright(c) 2006-2007, Ext JS, LLC.
37981  *
37982  * Originally Released Under LGPL - original licence link has changed is not relivant.
37983  *
37984  * Fork - LGPL
37985  * <script type="text/javascript">
37986  */
37987
37988 if(Roo.dd.DropZone){
37989     
37990 Roo.tree.TreeDropZone = function(tree, config){
37991     this.allowParentInsert = false;
37992     this.allowContainerDrop = false;
37993     this.appendOnly = false;
37994     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37995     this.tree = tree;
37996     this.lastInsertClass = "x-tree-no-status";
37997     this.dragOverData = {};
37998 };
37999
38000 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
38001     ddGroup : "TreeDD",
38002     scroll:  true,
38003     
38004     expandDelay : 1000,
38005     
38006     expandNode : function(node){
38007         if(node.hasChildNodes() && !node.isExpanded()){
38008             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
38009         }
38010     },
38011     
38012     queueExpand : function(node){
38013         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
38014     },
38015     
38016     cancelExpand : function(){
38017         if(this.expandProcId){
38018             clearTimeout(this.expandProcId);
38019             this.expandProcId = false;
38020         }
38021     },
38022     
38023     isValidDropPoint : function(n, pt, dd, e, data){
38024         if(!n || !data){ return false; }
38025         var targetNode = n.node;
38026         var dropNode = data.node;
38027         // default drop rules
38028         if(!(targetNode && targetNode.isTarget && pt)){
38029             return false;
38030         }
38031         if(pt == "append" && targetNode.allowChildren === false){
38032             return false;
38033         }
38034         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
38035             return false;
38036         }
38037         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
38038             return false;
38039         }
38040         // reuse the object
38041         var overEvent = this.dragOverData;
38042         overEvent.tree = this.tree;
38043         overEvent.target = targetNode;
38044         overEvent.data = data;
38045         overEvent.point = pt;
38046         overEvent.source = dd;
38047         overEvent.rawEvent = e;
38048         overEvent.dropNode = dropNode;
38049         overEvent.cancel = false;  
38050         var result = this.tree.fireEvent("nodedragover", overEvent);
38051         return overEvent.cancel === false && result !== false;
38052     },
38053     
38054     getDropPoint : function(e, n, dd)
38055     {
38056         var tn = n.node;
38057         if(tn.isRoot){
38058             return tn.allowChildren !== false ? "append" : false; // always append for root
38059         }
38060         var dragEl = n.ddel;
38061         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
38062         var y = Roo.lib.Event.getPageY(e);
38063         //var noAppend = tn.allowChildren === false || tn.isLeaf();
38064         
38065         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
38066         var noAppend = tn.allowChildren === false;
38067         if(this.appendOnly || tn.parentNode.allowChildren === false){
38068             return noAppend ? false : "append";
38069         }
38070         var noBelow = false;
38071         if(!this.allowParentInsert){
38072             noBelow = tn.hasChildNodes() && tn.isExpanded();
38073         }
38074         var q = (b - t) / (noAppend ? 2 : 3);
38075         if(y >= t && y < (t + q)){
38076             return "above";
38077         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
38078             return "below";
38079         }else{
38080             return "append";
38081         }
38082     },
38083     
38084     onNodeEnter : function(n, dd, e, data)
38085     {
38086         this.cancelExpand();
38087     },
38088     
38089     onNodeOver : function(n, dd, e, data)
38090     {
38091        
38092         var pt = this.getDropPoint(e, n, dd);
38093         var node = n.node;
38094         
38095         // auto node expand check
38096         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
38097             this.queueExpand(node);
38098         }else if(pt != "append"){
38099             this.cancelExpand();
38100         }
38101         
38102         // set the insert point style on the target node
38103         var returnCls = this.dropNotAllowed;
38104         if(this.isValidDropPoint(n, pt, dd, e, data)){
38105            if(pt){
38106                var el = n.ddel;
38107                var cls;
38108                if(pt == "above"){
38109                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
38110                    cls = "x-tree-drag-insert-above";
38111                }else if(pt == "below"){
38112                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
38113                    cls = "x-tree-drag-insert-below";
38114                }else{
38115                    returnCls = "x-tree-drop-ok-append";
38116                    cls = "x-tree-drag-append";
38117                }
38118                if(this.lastInsertClass != cls){
38119                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
38120                    this.lastInsertClass = cls;
38121                }
38122            }
38123        }
38124        return returnCls;
38125     },
38126     
38127     onNodeOut : function(n, dd, e, data){
38128         
38129         this.cancelExpand();
38130         this.removeDropIndicators(n);
38131     },
38132     
38133     onNodeDrop : function(n, dd, e, data){
38134         var point = this.getDropPoint(e, n, dd);
38135         var targetNode = n.node;
38136         targetNode.ui.startDrop();
38137         if(!this.isValidDropPoint(n, point, dd, e, data)){
38138             targetNode.ui.endDrop();
38139             return false;
38140         }
38141         // first try to find the drop node
38142         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
38143         var dropEvent = {
38144             tree : this.tree,
38145             target: targetNode,
38146             data: data,
38147             point: point,
38148             source: dd,
38149             rawEvent: e,
38150             dropNode: dropNode,
38151             cancel: !dropNode   
38152         };
38153         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
38154         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
38155             targetNode.ui.endDrop();
38156             return false;
38157         }
38158         // allow target changing
38159         targetNode = dropEvent.target;
38160         if(point == "append" && !targetNode.isExpanded()){
38161             targetNode.expand(false, null, function(){
38162                 this.completeDrop(dropEvent);
38163             }.createDelegate(this));
38164         }else{
38165             this.completeDrop(dropEvent);
38166         }
38167         return true;
38168     },
38169     
38170     completeDrop : function(de){
38171         var ns = de.dropNode, p = de.point, t = de.target;
38172         if(!(ns instanceof Array)){
38173             ns = [ns];
38174         }
38175         var n;
38176         for(var i = 0, len = ns.length; i < len; i++){
38177             n = ns[i];
38178             if(p == "above"){
38179                 t.parentNode.insertBefore(n, t);
38180             }else if(p == "below"){
38181                 t.parentNode.insertBefore(n, t.nextSibling);
38182             }else{
38183                 t.appendChild(n);
38184             }
38185         }
38186         n.ui.focus();
38187         if(this.tree.hlDrop){
38188             n.ui.highlight();
38189         }
38190         t.ui.endDrop();
38191         this.tree.fireEvent("nodedrop", de);
38192     },
38193     
38194     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
38195         if(this.tree.hlDrop){
38196             dropNode.ui.focus();
38197             dropNode.ui.highlight();
38198         }
38199         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
38200     },
38201     
38202     getTree : function(){
38203         return this.tree;
38204     },
38205     
38206     removeDropIndicators : function(n){
38207         if(n && n.ddel){
38208             var el = n.ddel;
38209             Roo.fly(el).removeClass([
38210                     "x-tree-drag-insert-above",
38211                     "x-tree-drag-insert-below",
38212                     "x-tree-drag-append"]);
38213             this.lastInsertClass = "_noclass";
38214         }
38215     },
38216     
38217     beforeDragDrop : function(target, e, id){
38218         this.cancelExpand();
38219         return true;
38220     },
38221     
38222     afterRepair : function(data){
38223         if(data && Roo.enableFx){
38224             data.node.ui.highlight();
38225         }
38226         this.hideProxy();
38227     } 
38228     
38229 });
38230
38231 }
38232 /*
38233  * Based on:
38234  * Ext JS Library 1.1.1
38235  * Copyright(c) 2006-2007, Ext JS, LLC.
38236  *
38237  * Originally Released Under LGPL - original licence link has changed is not relivant.
38238  *
38239  * Fork - LGPL
38240  * <script type="text/javascript">
38241  */
38242  
38243
38244 if(Roo.dd.DragZone){
38245 Roo.tree.TreeDragZone = function(tree, config){
38246     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
38247     this.tree = tree;
38248 };
38249
38250 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
38251     ddGroup : "TreeDD",
38252    
38253     onBeforeDrag : function(data, e){
38254         var n = data.node;
38255         return n && n.draggable && !n.disabled;
38256     },
38257      
38258     
38259     onInitDrag : function(e){
38260         var data = this.dragData;
38261         this.tree.getSelectionModel().select(data.node);
38262         this.proxy.update("");
38263         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
38264         this.tree.fireEvent("startdrag", this.tree, data.node, e);
38265     },
38266     
38267     getRepairXY : function(e, data){
38268         return data.node.ui.getDDRepairXY();
38269     },
38270     
38271     onEndDrag : function(data, e){
38272         this.tree.fireEvent("enddrag", this.tree, data.node, e);
38273         
38274         
38275     },
38276     
38277     onValidDrop : function(dd, e, id){
38278         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
38279         this.hideProxy();
38280     },
38281     
38282     beforeInvalidDrop : function(e, id){
38283         // this scrolls the original position back into view
38284         var sm = this.tree.getSelectionModel();
38285         sm.clearSelections();
38286         sm.select(this.dragData.node);
38287     }
38288 });
38289 }/*
38290  * Based on:
38291  * Ext JS Library 1.1.1
38292  * Copyright(c) 2006-2007, Ext JS, LLC.
38293  *
38294  * Originally Released Under LGPL - original licence link has changed is not relivant.
38295  *
38296  * Fork - LGPL
38297  * <script type="text/javascript">
38298  */
38299 /**
38300  * @class Roo.tree.TreeEditor
38301  * @extends Roo.Editor
38302  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
38303  * as the editor field.
38304  * @constructor
38305  * @param {Object} config (used to be the tree panel.)
38306  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
38307  * 
38308  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
38309  * @cfg {Roo.form.TextField} field [required] The field configuration
38310  *
38311  * 
38312  */
38313 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
38314     var tree = config;
38315     var field;
38316     if (oldconfig) { // old style..
38317         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
38318     } else {
38319         // new style..
38320         tree = config.tree;
38321         config.field = config.field  || {};
38322         config.field.xtype = 'TextField';
38323         field = Roo.factory(config.field, Roo.form);
38324     }
38325     config = config || {};
38326     
38327     
38328     this.addEvents({
38329         /**
38330          * @event beforenodeedit
38331          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
38332          * false from the handler of this event.
38333          * @param {Editor} this
38334          * @param {Roo.tree.Node} node 
38335          */
38336         "beforenodeedit" : true
38337     });
38338     
38339     //Roo.log(config);
38340     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
38341
38342     this.tree = tree;
38343
38344     tree.on('beforeclick', this.beforeNodeClick, this);
38345     tree.getTreeEl().on('mousedown', this.hide, this);
38346     this.on('complete', this.updateNode, this);
38347     this.on('beforestartedit', this.fitToTree, this);
38348     this.on('startedit', this.bindScroll, this, {delay:10});
38349     this.on('specialkey', this.onSpecialKey, this);
38350 };
38351
38352 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38353     /**
38354      * @cfg {String} alignment
38355      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38356      */
38357     alignment: "l-l",
38358     // inherit
38359     autoSize: false,
38360     /**
38361      * @cfg {Boolean} hideEl
38362      * True to hide the bound element while the editor is displayed (defaults to false)
38363      */
38364     hideEl : false,
38365     /**
38366      * @cfg {String} cls
38367      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38368      */
38369     cls: "x-small-editor x-tree-editor",
38370     /**
38371      * @cfg {Boolean} shim
38372      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38373      */
38374     shim:false,
38375     // inherit
38376     shadow:"frame",
38377     /**
38378      * @cfg {Number} maxWidth
38379      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38380      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38381      * scroll and client offsets into account prior to each edit.
38382      */
38383     maxWidth: 250,
38384
38385     editDelay : 350,
38386
38387     // private
38388     fitToTree : function(ed, el){
38389         var td = this.tree.getTreeEl().dom, nd = el.dom;
38390         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38391             td.scrollLeft = nd.offsetLeft;
38392         }
38393         var w = Math.min(
38394                 this.maxWidth,
38395                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38396         this.setSize(w, '');
38397         
38398         return this.fireEvent('beforenodeedit', this, this.editNode);
38399         
38400     },
38401
38402     // private
38403     triggerEdit : function(node){
38404         this.completeEdit();
38405         this.editNode = node;
38406         this.startEdit(node.ui.textNode, node.text);
38407     },
38408
38409     // private
38410     bindScroll : function(){
38411         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38412     },
38413
38414     // private
38415     beforeNodeClick : function(node, e){
38416         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38417         this.lastClick = new Date();
38418         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38419             e.stopEvent();
38420             this.triggerEdit(node);
38421             return false;
38422         }
38423         return true;
38424     },
38425
38426     // private
38427     updateNode : function(ed, value){
38428         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38429         this.editNode.setText(value);
38430     },
38431
38432     // private
38433     onHide : function(){
38434         Roo.tree.TreeEditor.superclass.onHide.call(this);
38435         if(this.editNode){
38436             this.editNode.ui.focus();
38437         }
38438     },
38439
38440     // private
38441     onSpecialKey : function(field, e){
38442         var k = e.getKey();
38443         if(k == e.ESC){
38444             e.stopEvent();
38445             this.cancelEdit();
38446         }else if(k == e.ENTER && !e.hasModifier()){
38447             e.stopEvent();
38448             this.completeEdit();
38449         }
38450     }
38451 });//<Script type="text/javascript">
38452 /*
38453  * Based on:
38454  * Ext JS Library 1.1.1
38455  * Copyright(c) 2006-2007, Ext JS, LLC.
38456  *
38457  * Originally Released Under LGPL - original licence link has changed is not relivant.
38458  *
38459  * Fork - LGPL
38460  * <script type="text/javascript">
38461  */
38462  
38463 /**
38464  * Not documented??? - probably should be...
38465  */
38466
38467 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38468     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38469     
38470     renderElements : function(n, a, targetNode, bulkRender){
38471         //consel.log("renderElements?");
38472         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38473
38474         var t = n.getOwnerTree();
38475         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38476         
38477         var cols = t.columns;
38478         var bw = t.borderWidth;
38479         var c = cols[0];
38480         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38481          var cb = typeof a.checked == "boolean";
38482         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38483         var colcls = 'x-t-' + tid + '-c0';
38484         var buf = [
38485             '<li class="x-tree-node">',
38486             
38487                 
38488                 '<div class="x-tree-node-el ', a.cls,'">',
38489                     // extran...
38490                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38491                 
38492                 
38493                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38494                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38495                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38496                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38497                            (a.iconCls ? ' '+a.iconCls : ''),
38498                            '" unselectable="on" />',
38499                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38500                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38501                              
38502                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38503                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38504                             '<span unselectable="on" qtip="' + tx + '">',
38505                              tx,
38506                              '</span></a>' ,
38507                     '</div>',
38508                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38509                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38510                  ];
38511         for(var i = 1, len = cols.length; i < len; i++){
38512             c = cols[i];
38513             colcls = 'x-t-' + tid + '-c' +i;
38514             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38515             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38516                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38517                       "</div>");
38518          }
38519          
38520          buf.push(
38521             '</a>',
38522             '<div class="x-clear"></div></div>',
38523             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38524             "</li>");
38525         
38526         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38527             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38528                                 n.nextSibling.ui.getEl(), buf.join(""));
38529         }else{
38530             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38531         }
38532         var el = this.wrap.firstChild;
38533         this.elRow = el;
38534         this.elNode = el.firstChild;
38535         this.ranchor = el.childNodes[1];
38536         this.ctNode = this.wrap.childNodes[1];
38537         var cs = el.firstChild.childNodes;
38538         this.indentNode = cs[0];
38539         this.ecNode = cs[1];
38540         this.iconNode = cs[2];
38541         var index = 3;
38542         if(cb){
38543             this.checkbox = cs[3];
38544             index++;
38545         }
38546         this.anchor = cs[index];
38547         
38548         this.textNode = cs[index].firstChild;
38549         
38550         //el.on("click", this.onClick, this);
38551         //el.on("dblclick", this.onDblClick, this);
38552         
38553         
38554        // console.log(this);
38555     },
38556     initEvents : function(){
38557         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38558         
38559             
38560         var a = this.ranchor;
38561
38562         var el = Roo.get(a);
38563
38564         if(Roo.isOpera){ // opera render bug ignores the CSS
38565             el.setStyle("text-decoration", "none");
38566         }
38567
38568         el.on("click", this.onClick, this);
38569         el.on("dblclick", this.onDblClick, this);
38570         el.on("contextmenu", this.onContextMenu, this);
38571         
38572     },
38573     
38574     /*onSelectedChange : function(state){
38575         if(state){
38576             this.focus();
38577             this.addClass("x-tree-selected");
38578         }else{
38579             //this.blur();
38580             this.removeClass("x-tree-selected");
38581         }
38582     },*/
38583     addClass : function(cls){
38584         if(this.elRow){
38585             Roo.fly(this.elRow).addClass(cls);
38586         }
38587         
38588     },
38589     
38590     
38591     removeClass : function(cls){
38592         if(this.elRow){
38593             Roo.fly(this.elRow).removeClass(cls);
38594         }
38595     }
38596
38597     
38598     
38599 });//<Script type="text/javascript">
38600
38601 /*
38602  * Based on:
38603  * Ext JS Library 1.1.1
38604  * Copyright(c) 2006-2007, Ext JS, LLC.
38605  *
38606  * Originally Released Under LGPL - original licence link has changed is not relivant.
38607  *
38608  * Fork - LGPL
38609  * <script type="text/javascript">
38610  */
38611  
38612
38613 /**
38614  * @class Roo.tree.ColumnTree
38615  * @extends Roo.tree.TreePanel
38616  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38617  * @cfg {int} borderWidth  compined right/left border allowance
38618  * @constructor
38619  * @param {String/HTMLElement/Element} el The container element
38620  * @param {Object} config
38621  */
38622 Roo.tree.ColumnTree =  function(el, config)
38623 {
38624    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38625    this.addEvents({
38626         /**
38627         * @event resize
38628         * Fire this event on a container when it resizes
38629         * @param {int} w Width
38630         * @param {int} h Height
38631         */
38632        "resize" : true
38633     });
38634     this.on('resize', this.onResize, this);
38635 };
38636
38637 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38638     //lines:false,
38639     
38640     
38641     borderWidth: Roo.isBorderBox ? 0 : 2, 
38642     headEls : false,
38643     
38644     render : function(){
38645         // add the header.....
38646        
38647         Roo.tree.ColumnTree.superclass.render.apply(this);
38648         
38649         this.el.addClass('x-column-tree');
38650         
38651         this.headers = this.el.createChild(
38652             {cls:'x-tree-headers'},this.innerCt.dom);
38653    
38654         var cols = this.columns, c;
38655         var totalWidth = 0;
38656         this.headEls = [];
38657         var  len = cols.length;
38658         for(var i = 0; i < len; i++){
38659              c = cols[i];
38660              totalWidth += c.width;
38661             this.headEls.push(this.headers.createChild({
38662                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38663                  cn: {
38664                      cls:'x-tree-hd-text',
38665                      html: c.header
38666                  },
38667                  style:'width:'+(c.width-this.borderWidth)+'px;'
38668              }));
38669         }
38670         this.headers.createChild({cls:'x-clear'});
38671         // prevent floats from wrapping when clipped
38672         this.headers.setWidth(totalWidth);
38673         //this.innerCt.setWidth(totalWidth);
38674         this.innerCt.setStyle({ overflow: 'auto' });
38675         this.onResize(this.width, this.height);
38676              
38677         
38678     },
38679     onResize : function(w,h)
38680     {
38681         this.height = h;
38682         this.width = w;
38683         // resize cols..
38684         this.innerCt.setWidth(this.width);
38685         this.innerCt.setHeight(this.height-20);
38686         
38687         // headers...
38688         var cols = this.columns, c;
38689         var totalWidth = 0;
38690         var expEl = false;
38691         var len = cols.length;
38692         for(var i = 0; i < len; i++){
38693             c = cols[i];
38694             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38695                 // it's the expander..
38696                 expEl  = this.headEls[i];
38697                 continue;
38698             }
38699             totalWidth += c.width;
38700             
38701         }
38702         if (expEl) {
38703             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38704         }
38705         this.headers.setWidth(w-20);
38706
38707         
38708         
38709         
38710     }
38711 });
38712 /*
38713  * Based on:
38714  * Ext JS Library 1.1.1
38715  * Copyright(c) 2006-2007, Ext JS, LLC.
38716  *
38717  * Originally Released Under LGPL - original licence link has changed is not relivant.
38718  *
38719  * Fork - LGPL
38720  * <script type="text/javascript">
38721  */
38722  
38723 /**
38724  * @class Roo.menu.Menu
38725  * @extends Roo.util.Observable
38726  * @children Roo.menu.Item Roo.menu.Separator Roo.menu.TextItem
38727  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38728  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38729  * @constructor
38730  * Creates a new Menu
38731  * @param {Object} config Configuration options
38732  */
38733 Roo.menu.Menu = function(config){
38734     
38735     Roo.menu.Menu.superclass.constructor.call(this, config);
38736     
38737     this.id = this.id || Roo.id();
38738     this.addEvents({
38739         /**
38740          * @event beforeshow
38741          * Fires before this menu is displayed
38742          * @param {Roo.menu.Menu} this
38743          */
38744         beforeshow : true,
38745         /**
38746          * @event beforehide
38747          * Fires before this menu is hidden
38748          * @param {Roo.menu.Menu} this
38749          */
38750         beforehide : true,
38751         /**
38752          * @event show
38753          * Fires after this menu is displayed
38754          * @param {Roo.menu.Menu} this
38755          */
38756         show : true,
38757         /**
38758          * @event hide
38759          * Fires after this menu is hidden
38760          * @param {Roo.menu.Menu} this
38761          */
38762         hide : true,
38763         /**
38764          * @event click
38765          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38766          * @param {Roo.menu.Menu} this
38767          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38768          * @param {Roo.EventObject} e
38769          */
38770         click : true,
38771         /**
38772          * @event mouseover
38773          * Fires when the mouse is hovering over this menu
38774          * @param {Roo.menu.Menu} this
38775          * @param {Roo.EventObject} e
38776          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38777          */
38778         mouseover : true,
38779         /**
38780          * @event mouseout
38781          * Fires when the mouse exits this menu
38782          * @param {Roo.menu.Menu} this
38783          * @param {Roo.EventObject} e
38784          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38785          */
38786         mouseout : true,
38787         /**
38788          * @event itemclick
38789          * Fires when a menu item contained in this menu is clicked
38790          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38791          * @param {Roo.EventObject} e
38792          */
38793         itemclick: true
38794     });
38795     if (this.registerMenu) {
38796         Roo.menu.MenuMgr.register(this);
38797     }
38798     
38799     var mis = this.items;
38800     this.items = new Roo.util.MixedCollection();
38801     if(mis){
38802         this.add.apply(this, mis);
38803     }
38804 };
38805
38806 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38807     /**
38808      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38809      */
38810     minWidth : 120,
38811     /**
38812      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38813      * for bottom-right shadow (defaults to "sides")
38814      */
38815     shadow : "sides",
38816     /**
38817      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38818      * this menu (defaults to "tl-tr?")
38819      */
38820     subMenuAlign : "tl-tr?",
38821     /**
38822      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38823      * relative to its element of origin (defaults to "tl-bl?")
38824      */
38825     defaultAlign : "tl-bl?",
38826     /**
38827      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38828      */
38829     allowOtherMenus : false,
38830     /**
38831      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38832      */
38833     registerMenu : true,
38834
38835     hidden:true,
38836
38837     // private
38838     render : function(){
38839         if(this.el){
38840             return;
38841         }
38842         var el = this.el = new Roo.Layer({
38843             cls: "x-menu",
38844             shadow:this.shadow,
38845             constrain: false,
38846             parentEl: this.parentEl || document.body,
38847             zindex:15000
38848         });
38849
38850         this.keyNav = new Roo.menu.MenuNav(this);
38851
38852         if(this.plain){
38853             el.addClass("x-menu-plain");
38854         }
38855         if(this.cls){
38856             el.addClass(this.cls);
38857         }
38858         // generic focus element
38859         this.focusEl = el.createChild({
38860             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38861         });
38862         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38863         //disabling touch- as it's causing issues ..
38864         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38865         ul.on('click'   , this.onClick, this);
38866         
38867         
38868         ul.on("mouseover", this.onMouseOver, this);
38869         ul.on("mouseout", this.onMouseOut, this);
38870         this.items.each(function(item){
38871             if (item.hidden) {
38872                 return;
38873             }
38874             
38875             var li = document.createElement("li");
38876             li.className = "x-menu-list-item";
38877             ul.dom.appendChild(li);
38878             item.render(li, this);
38879         }, this);
38880         this.ul = ul;
38881         this.autoWidth();
38882     },
38883
38884     // private
38885     autoWidth : function(){
38886         var el = this.el, ul = this.ul;
38887         if(!el){
38888             return;
38889         }
38890         var w = this.width;
38891         if(w){
38892             el.setWidth(w);
38893         }else if(Roo.isIE){
38894             el.setWidth(this.minWidth);
38895             var t = el.dom.offsetWidth; // force recalc
38896             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38897         }
38898     },
38899
38900     // private
38901     delayAutoWidth : function(){
38902         if(this.rendered){
38903             if(!this.awTask){
38904                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38905             }
38906             this.awTask.delay(20);
38907         }
38908     },
38909
38910     // private
38911     findTargetItem : function(e){
38912         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38913         if(t && t.menuItemId){
38914             return this.items.get(t.menuItemId);
38915         }
38916     },
38917
38918     // private
38919     onClick : function(e){
38920         Roo.log("menu.onClick");
38921         var t = this.findTargetItem(e);
38922         if(!t){
38923             return;
38924         }
38925         Roo.log(e);
38926         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38927             if(t == this.activeItem && t.shouldDeactivate(e)){
38928                 this.activeItem.deactivate();
38929                 delete this.activeItem;
38930                 return;
38931             }
38932             if(t.canActivate){
38933                 this.setActiveItem(t, true);
38934             }
38935             return;
38936             
38937             
38938         }
38939         
38940         t.onClick(e);
38941         this.fireEvent("click", this, t, e);
38942     },
38943
38944     // private
38945     setActiveItem : function(item, autoExpand){
38946         if(item != this.activeItem){
38947             if(this.activeItem){
38948                 this.activeItem.deactivate();
38949             }
38950             this.activeItem = item;
38951             item.activate(autoExpand);
38952         }else if(autoExpand){
38953             item.expandMenu();
38954         }
38955     },
38956
38957     // private
38958     tryActivate : function(start, step){
38959         var items = this.items;
38960         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38961             var item = items.get(i);
38962             if(!item.disabled && item.canActivate){
38963                 this.setActiveItem(item, false);
38964                 return item;
38965             }
38966         }
38967         return false;
38968     },
38969
38970     // private
38971     onMouseOver : function(e){
38972         var t;
38973         if(t = this.findTargetItem(e)){
38974             if(t.canActivate && !t.disabled){
38975                 this.setActiveItem(t, true);
38976             }
38977         }
38978         this.fireEvent("mouseover", this, e, t);
38979     },
38980
38981     // private
38982     onMouseOut : function(e){
38983         var t;
38984         if(t = this.findTargetItem(e)){
38985             if(t == this.activeItem && t.shouldDeactivate(e)){
38986                 this.activeItem.deactivate();
38987                 delete this.activeItem;
38988             }
38989         }
38990         this.fireEvent("mouseout", this, e, t);
38991     },
38992
38993     /**
38994      * Read-only.  Returns true if the menu is currently displayed, else false.
38995      * @type Boolean
38996      */
38997     isVisible : function(){
38998         return this.el && !this.hidden;
38999     },
39000
39001     /**
39002      * Displays this menu relative to another element
39003      * @param {String/HTMLElement/Roo.Element} element The element to align to
39004      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
39005      * the element (defaults to this.defaultAlign)
39006      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39007      */
39008     show : function(el, pos, parentMenu){
39009         this.parentMenu = parentMenu;
39010         if(!this.el){
39011             this.render();
39012         }
39013         this.fireEvent("beforeshow", this);
39014         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
39015     },
39016
39017     /**
39018      * Displays this menu at a specific xy position
39019      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
39020      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
39021      */
39022     showAt : function(xy, parentMenu, /* private: */_e){
39023         this.parentMenu = parentMenu;
39024         if(!this.el){
39025             this.render();
39026         }
39027         if(_e !== false){
39028             this.fireEvent("beforeshow", this);
39029             xy = this.el.adjustForConstraints(xy);
39030         }
39031         this.el.setXY(xy);
39032         this.el.show();
39033         this.hidden = false;
39034         this.focus();
39035         this.fireEvent("show", this);
39036     },
39037
39038     focus : function(){
39039         if(!this.hidden){
39040             this.doFocus.defer(50, this);
39041         }
39042     },
39043
39044     doFocus : function(){
39045         if(!this.hidden){
39046             this.focusEl.focus();
39047         }
39048     },
39049
39050     /**
39051      * Hides this menu and optionally all parent menus
39052      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
39053      */
39054     hide : function(deep){
39055         if(this.el && this.isVisible()){
39056             this.fireEvent("beforehide", this);
39057             if(this.activeItem){
39058                 this.activeItem.deactivate();
39059                 this.activeItem = null;
39060             }
39061             this.el.hide();
39062             this.hidden = true;
39063             this.fireEvent("hide", this);
39064         }
39065         if(deep === true && this.parentMenu){
39066             this.parentMenu.hide(true);
39067         }
39068     },
39069
39070     /**
39071      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
39072      * Any of the following are valid:
39073      * <ul>
39074      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
39075      * <li>An HTMLElement object which will be converted to a menu item</li>
39076      * <li>A menu item config object that will be created as a new menu item</li>
39077      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
39078      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
39079      * </ul>
39080      * Usage:
39081      * <pre><code>
39082 // Create the menu
39083 var menu = new Roo.menu.Menu();
39084
39085 // Create a menu item to add by reference
39086 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
39087
39088 // Add a bunch of items at once using different methods.
39089 // Only the last item added will be returned.
39090 var item = menu.add(
39091     menuItem,                // add existing item by ref
39092     'Dynamic Item',          // new TextItem
39093     '-',                     // new separator
39094     { text: 'Config Item' }  // new item by config
39095 );
39096 </code></pre>
39097      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
39098      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
39099      */
39100     add : function(){
39101         var a = arguments, l = a.length, item;
39102         for(var i = 0; i < l; i++){
39103             var el = a[i];
39104             if ((typeof(el) == "object") && el.xtype && el.xns) {
39105                 el = Roo.factory(el, Roo.menu);
39106             }
39107             
39108             if(el.render){ // some kind of Item
39109                 item = this.addItem(el);
39110             }else if(typeof el == "string"){ // string
39111                 if(el == "separator" || el == "-"){
39112                     item = this.addSeparator();
39113                 }else{
39114                     item = this.addText(el);
39115                 }
39116             }else if(el.tagName || el.el){ // element
39117                 item = this.addElement(el);
39118             }else if(typeof el == "object"){ // must be menu item config?
39119                 item = this.addMenuItem(el);
39120             }
39121         }
39122         return item;
39123     },
39124
39125     /**
39126      * Returns this menu's underlying {@link Roo.Element} object
39127      * @return {Roo.Element} The element
39128      */
39129     getEl : function(){
39130         if(!this.el){
39131             this.render();
39132         }
39133         return this.el;
39134     },
39135
39136     /**
39137      * Adds a separator bar to the menu
39138      * @return {Roo.menu.Item} The menu item that was added
39139      */
39140     addSeparator : function(){
39141         return this.addItem(new Roo.menu.Separator());
39142     },
39143
39144     /**
39145      * Adds an {@link Roo.Element} object to the menu
39146      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
39147      * @return {Roo.menu.Item} The menu item that was added
39148      */
39149     addElement : function(el){
39150         return this.addItem(new Roo.menu.BaseItem(el));
39151     },
39152
39153     /**
39154      * Adds an existing object based on {@link Roo.menu.Item} to the menu
39155      * @param {Roo.menu.Item} item The menu item to add
39156      * @return {Roo.menu.Item} The menu item that was added
39157      */
39158     addItem : function(item){
39159         this.items.add(item);
39160         if(this.ul){
39161             var li = document.createElement("li");
39162             li.className = "x-menu-list-item";
39163             this.ul.dom.appendChild(li);
39164             item.render(li, this);
39165             this.delayAutoWidth();
39166         }
39167         return item;
39168     },
39169
39170     /**
39171      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
39172      * @param {Object} config A MenuItem config object
39173      * @return {Roo.menu.Item} The menu item that was added
39174      */
39175     addMenuItem : function(config){
39176         if(!(config instanceof Roo.menu.Item)){
39177             if(typeof config.checked == "boolean"){ // must be check menu item config?
39178                 config = new Roo.menu.CheckItem(config);
39179             }else{
39180                 config = new Roo.menu.Item(config);
39181             }
39182         }
39183         return this.addItem(config);
39184     },
39185
39186     /**
39187      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
39188      * @param {String} text The text to display in the menu item
39189      * @return {Roo.menu.Item} The menu item that was added
39190      */
39191     addText : function(text){
39192         return this.addItem(new Roo.menu.TextItem({ text : text }));
39193     },
39194
39195     /**
39196      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
39197      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
39198      * @param {Roo.menu.Item} item The menu item to add
39199      * @return {Roo.menu.Item} The menu item that was added
39200      */
39201     insert : function(index, item){
39202         this.items.insert(index, item);
39203         if(this.ul){
39204             var li = document.createElement("li");
39205             li.className = "x-menu-list-item";
39206             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
39207             item.render(li, this);
39208             this.delayAutoWidth();
39209         }
39210         return item;
39211     },
39212
39213     /**
39214      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
39215      * @param {Roo.menu.Item} item The menu item to remove
39216      */
39217     remove : function(item){
39218         this.items.removeKey(item.id);
39219         item.destroy();
39220     },
39221
39222     /**
39223      * Removes and destroys all items in the menu
39224      */
39225     removeAll : function(){
39226         var f;
39227         while(f = this.items.first()){
39228             this.remove(f);
39229         }
39230     }
39231 });
39232
39233 // MenuNav is a private utility class used internally by the Menu
39234 Roo.menu.MenuNav = function(menu){
39235     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
39236     this.scope = this.menu = menu;
39237 };
39238
39239 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
39240     doRelay : function(e, h){
39241         var k = e.getKey();
39242         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
39243             this.menu.tryActivate(0, 1);
39244             return false;
39245         }
39246         return h.call(this.scope || this, e, this.menu);
39247     },
39248
39249     up : function(e, m){
39250         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
39251             m.tryActivate(m.items.length-1, -1);
39252         }
39253     },
39254
39255     down : function(e, m){
39256         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
39257             m.tryActivate(0, 1);
39258         }
39259     },
39260
39261     right : function(e, m){
39262         if(m.activeItem){
39263             m.activeItem.expandMenu(true);
39264         }
39265     },
39266
39267     left : function(e, m){
39268         m.hide();
39269         if(m.parentMenu && m.parentMenu.activeItem){
39270             m.parentMenu.activeItem.activate();
39271         }
39272     },
39273
39274     enter : function(e, m){
39275         if(m.activeItem){
39276             e.stopPropagation();
39277             m.activeItem.onClick(e);
39278             m.fireEvent("click", this, m.activeItem);
39279             return true;
39280         }
39281     }
39282 });/*
39283  * Based on:
39284  * Ext JS Library 1.1.1
39285  * Copyright(c) 2006-2007, Ext JS, LLC.
39286  *
39287  * Originally Released Under LGPL - original licence link has changed is not relivant.
39288  *
39289  * Fork - LGPL
39290  * <script type="text/javascript">
39291  */
39292  
39293 /**
39294  * @class Roo.menu.MenuMgr
39295  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
39296  * @static
39297  */
39298 Roo.menu.MenuMgr = function(){
39299    var menus, active, groups = {}, attached = false, lastShow = new Date();
39300
39301    // private - called when first menu is created
39302    function init(){
39303        menus = {};
39304        active = new Roo.util.MixedCollection();
39305        Roo.get(document).addKeyListener(27, function(){
39306            if(active.length > 0){
39307                hideAll();
39308            }
39309        });
39310    }
39311
39312    // private
39313    function hideAll(){
39314        if(active && active.length > 0){
39315            var c = active.clone();
39316            c.each(function(m){
39317                m.hide();
39318            });
39319        }
39320    }
39321
39322    // private
39323    function onHide(m){
39324        active.remove(m);
39325        if(active.length < 1){
39326            Roo.get(document).un("mousedown", onMouseDown);
39327            attached = false;
39328        }
39329    }
39330
39331    // private
39332    function onShow(m){
39333        var last = active.last();
39334        lastShow = new Date();
39335        active.add(m);
39336        if(!attached){
39337            Roo.get(document).on("mousedown", onMouseDown);
39338            attached = true;
39339        }
39340        if(m.parentMenu){
39341           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
39342           m.parentMenu.activeChild = m;
39343        }else if(last && last.isVisible()){
39344           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
39345        }
39346    }
39347
39348    // private
39349    function onBeforeHide(m){
39350        if(m.activeChild){
39351            m.activeChild.hide();
39352        }
39353        if(m.autoHideTimer){
39354            clearTimeout(m.autoHideTimer);
39355            delete m.autoHideTimer;
39356        }
39357    }
39358
39359    // private
39360    function onBeforeShow(m){
39361        var pm = m.parentMenu;
39362        if(!pm && !m.allowOtherMenus){
39363            hideAll();
39364        }else if(pm && pm.activeChild && active != m){
39365            pm.activeChild.hide();
39366        }
39367    }
39368
39369    // private
39370    function onMouseDown(e){
39371        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39372            hideAll();
39373        }
39374    }
39375
39376    // private
39377    function onBeforeCheck(mi, state){
39378        if(state){
39379            var g = groups[mi.group];
39380            for(var i = 0, l = g.length; i < l; i++){
39381                if(g[i] != mi){
39382                    g[i].setChecked(false);
39383                }
39384            }
39385        }
39386    }
39387
39388    return {
39389
39390        /**
39391         * Hides all menus that are currently visible
39392         */
39393        hideAll : function(){
39394             hideAll();  
39395        },
39396
39397        // private
39398        register : function(menu){
39399            if(!menus){
39400                init();
39401            }
39402            menus[menu.id] = menu;
39403            menu.on("beforehide", onBeforeHide);
39404            menu.on("hide", onHide);
39405            menu.on("beforeshow", onBeforeShow);
39406            menu.on("show", onShow);
39407            var g = menu.group;
39408            if(g && menu.events["checkchange"]){
39409                if(!groups[g]){
39410                    groups[g] = [];
39411                }
39412                groups[g].push(menu);
39413                menu.on("checkchange", onCheck);
39414            }
39415        },
39416
39417         /**
39418          * Returns a {@link Roo.menu.Menu} object
39419          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39420          * be used to generate and return a new Menu instance.
39421          */
39422        get : function(menu){
39423            if(typeof menu == "string"){ // menu id
39424                return menus[menu];
39425            }else if(menu.events){  // menu instance
39426                return menu;
39427            }else if(typeof menu.length == 'number'){ // array of menu items?
39428                return new Roo.menu.Menu({items:menu});
39429            }else{ // otherwise, must be a config
39430                return new Roo.menu.Menu(menu);
39431            }
39432        },
39433
39434        // private
39435        unregister : function(menu){
39436            delete menus[menu.id];
39437            menu.un("beforehide", onBeforeHide);
39438            menu.un("hide", onHide);
39439            menu.un("beforeshow", onBeforeShow);
39440            menu.un("show", onShow);
39441            var g = menu.group;
39442            if(g && menu.events["checkchange"]){
39443                groups[g].remove(menu);
39444                menu.un("checkchange", onCheck);
39445            }
39446        },
39447
39448        // private
39449        registerCheckable : function(menuItem){
39450            var g = menuItem.group;
39451            if(g){
39452                if(!groups[g]){
39453                    groups[g] = [];
39454                }
39455                groups[g].push(menuItem);
39456                menuItem.on("beforecheckchange", onBeforeCheck);
39457            }
39458        },
39459
39460        // private
39461        unregisterCheckable : function(menuItem){
39462            var g = menuItem.group;
39463            if(g){
39464                groups[g].remove(menuItem);
39465                menuItem.un("beforecheckchange", onBeforeCheck);
39466            }
39467        }
39468    };
39469 }();/*
39470  * Based on:
39471  * Ext JS Library 1.1.1
39472  * Copyright(c) 2006-2007, Ext JS, LLC.
39473  *
39474  * Originally Released Under LGPL - original licence link has changed is not relivant.
39475  *
39476  * Fork - LGPL
39477  * <script type="text/javascript">
39478  */
39479  
39480
39481 /**
39482  * @class Roo.menu.BaseItem
39483  * @extends Roo.Component
39484  * @abstract
39485  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39486  * management and base configuration options shared by all menu components.
39487  * @constructor
39488  * Creates a new BaseItem
39489  * @param {Object} config Configuration options
39490  */
39491 Roo.menu.BaseItem = function(config){
39492     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39493
39494     this.addEvents({
39495         /**
39496          * @event click
39497          * Fires when this item is clicked
39498          * @param {Roo.menu.BaseItem} this
39499          * @param {Roo.EventObject} e
39500          */
39501         click: true,
39502         /**
39503          * @event activate
39504          * Fires when this item is activated
39505          * @param {Roo.menu.BaseItem} this
39506          */
39507         activate : true,
39508         /**
39509          * @event deactivate
39510          * Fires when this item is deactivated
39511          * @param {Roo.menu.BaseItem} this
39512          */
39513         deactivate : true
39514     });
39515
39516     if(this.handler){
39517         this.on("click", this.handler, this.scope, true);
39518     }
39519 };
39520
39521 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39522     /**
39523      * @cfg {Function} handler
39524      * A function that will handle the click event of this menu item (defaults to undefined)
39525      */
39526     /**
39527      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39528      */
39529     canActivate : false,
39530     
39531      /**
39532      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39533      */
39534     hidden: false,
39535     
39536     /**
39537      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39538      */
39539     activeClass : "x-menu-item-active",
39540     /**
39541      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39542      */
39543     hideOnClick : true,
39544     /**
39545      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39546      */
39547     hideDelay : 100,
39548
39549     // private
39550     ctype: "Roo.menu.BaseItem",
39551
39552     // private
39553     actionMode : "container",
39554
39555     // private
39556     render : function(container, parentMenu){
39557         this.parentMenu = parentMenu;
39558         Roo.menu.BaseItem.superclass.render.call(this, container);
39559         this.container.menuItemId = this.id;
39560     },
39561
39562     // private
39563     onRender : function(container, position){
39564         this.el = Roo.get(this.el);
39565         container.dom.appendChild(this.el.dom);
39566     },
39567
39568     // private
39569     onClick : function(e){
39570         if(!this.disabled && this.fireEvent("click", this, e) !== false
39571                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39572             this.handleClick(e);
39573         }else{
39574             e.stopEvent();
39575         }
39576     },
39577
39578     // private
39579     activate : function(){
39580         if(this.disabled){
39581             return false;
39582         }
39583         var li = this.container;
39584         li.addClass(this.activeClass);
39585         this.region = li.getRegion().adjust(2, 2, -2, -2);
39586         this.fireEvent("activate", this);
39587         return true;
39588     },
39589
39590     // private
39591     deactivate : function(){
39592         this.container.removeClass(this.activeClass);
39593         this.fireEvent("deactivate", this);
39594     },
39595
39596     // private
39597     shouldDeactivate : function(e){
39598         return !this.region || !this.region.contains(e.getPoint());
39599     },
39600
39601     // private
39602     handleClick : function(e){
39603         if(this.hideOnClick){
39604             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39605         }
39606     },
39607
39608     // private
39609     expandMenu : function(autoActivate){
39610         // do nothing
39611     },
39612
39613     // private
39614     hideMenu : function(){
39615         // do nothing
39616     }
39617 });/*
39618  * Based on:
39619  * Ext JS Library 1.1.1
39620  * Copyright(c) 2006-2007, Ext JS, LLC.
39621  *
39622  * Originally Released Under LGPL - original licence link has changed is not relivant.
39623  *
39624  * Fork - LGPL
39625  * <script type="text/javascript">
39626  */
39627  
39628 /**
39629  * @class Roo.menu.Adapter
39630  * @extends Roo.menu.BaseItem
39631  * @abstract
39632  * 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.
39633  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39634  * @constructor
39635  * Creates a new Adapter
39636  * @param {Object} config Configuration options
39637  */
39638 Roo.menu.Adapter = function(component, config){
39639     Roo.menu.Adapter.superclass.constructor.call(this, config);
39640     this.component = component;
39641 };
39642 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39643     // private
39644     canActivate : true,
39645
39646     // private
39647     onRender : function(container, position){
39648         this.component.render(container);
39649         this.el = this.component.getEl();
39650     },
39651
39652     // private
39653     activate : function(){
39654         if(this.disabled){
39655             return false;
39656         }
39657         this.component.focus();
39658         this.fireEvent("activate", this);
39659         return true;
39660     },
39661
39662     // private
39663     deactivate : function(){
39664         this.fireEvent("deactivate", this);
39665     },
39666
39667     // private
39668     disable : function(){
39669         this.component.disable();
39670         Roo.menu.Adapter.superclass.disable.call(this);
39671     },
39672
39673     // private
39674     enable : function(){
39675         this.component.enable();
39676         Roo.menu.Adapter.superclass.enable.call(this);
39677     }
39678 });/*
39679  * Based on:
39680  * Ext JS Library 1.1.1
39681  * Copyright(c) 2006-2007, Ext JS, LLC.
39682  *
39683  * Originally Released Under LGPL - original licence link has changed is not relivant.
39684  *
39685  * Fork - LGPL
39686  * <script type="text/javascript">
39687  */
39688
39689 /**
39690  * @class Roo.menu.TextItem
39691  * @extends Roo.menu.BaseItem
39692  * Adds a static text string to a menu, usually used as either a heading or group separator.
39693  * Note: old style constructor with text is still supported.
39694  * 
39695  * @constructor
39696  * Creates a new TextItem
39697  * @param {Object} cfg Configuration
39698  */
39699 Roo.menu.TextItem = function(cfg){
39700     if (typeof(cfg) == 'string') {
39701         this.text = cfg;
39702     } else {
39703         Roo.apply(this,cfg);
39704     }
39705     
39706     Roo.menu.TextItem.superclass.constructor.call(this);
39707 };
39708
39709 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39710     /**
39711      * @cfg {String} text Text to show on item.
39712      */
39713     text : '',
39714     
39715     /**
39716      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39717      */
39718     hideOnClick : false,
39719     /**
39720      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39721      */
39722     itemCls : "x-menu-text",
39723
39724     // private
39725     onRender : function(){
39726         var s = document.createElement("span");
39727         s.className = this.itemCls;
39728         s.innerHTML = this.text;
39729         this.el = s;
39730         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39731     }
39732 });/*
39733  * Based on:
39734  * Ext JS Library 1.1.1
39735  * Copyright(c) 2006-2007, Ext JS, LLC.
39736  *
39737  * Originally Released Under LGPL - original licence link has changed is not relivant.
39738  *
39739  * Fork - LGPL
39740  * <script type="text/javascript">
39741  */
39742
39743 /**
39744  * @class Roo.menu.Separator
39745  * @extends Roo.menu.BaseItem
39746  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39747  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39748  * @constructor
39749  * @param {Object} config Configuration options
39750  */
39751 Roo.menu.Separator = function(config){
39752     Roo.menu.Separator.superclass.constructor.call(this, config);
39753 };
39754
39755 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39756     /**
39757      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39758      */
39759     itemCls : "x-menu-sep",
39760     /**
39761      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39762      */
39763     hideOnClick : false,
39764
39765     // private
39766     onRender : function(li){
39767         var s = document.createElement("span");
39768         s.className = this.itemCls;
39769         s.innerHTML = "&#160;";
39770         this.el = s;
39771         li.addClass("x-menu-sep-li");
39772         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39773     }
39774 });/*
39775  * Based on:
39776  * Ext JS Library 1.1.1
39777  * Copyright(c) 2006-2007, Ext JS, LLC.
39778  *
39779  * Originally Released Under LGPL - original licence link has changed is not relivant.
39780  *
39781  * Fork - LGPL
39782  * <script type="text/javascript">
39783  */
39784 /**
39785  * @class Roo.menu.Item
39786  * @extends Roo.menu.BaseItem
39787  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39788  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39789  * activation and click handling.
39790  * @constructor
39791  * Creates a new Item
39792  * @param {Object} config Configuration options
39793  */
39794 Roo.menu.Item = function(config){
39795     Roo.menu.Item.superclass.constructor.call(this, config);
39796     if(this.menu){
39797         this.menu = Roo.menu.MenuMgr.get(this.menu);
39798     }
39799 };
39800 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39801     /**
39802      * @cfg {Roo.menu.Menu} menu
39803      * A Sub menu
39804      */
39805     /**
39806      * @cfg {String} text
39807      * The text to show on the menu item.
39808      */
39809     text: '',
39810      /**
39811      * @cfg {String} HTML to render in menu
39812      * The text to show on the menu item (HTML version).
39813      */
39814     html: '',
39815     /**
39816      * @cfg {String} icon
39817      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39818      */
39819     icon: undefined,
39820     /**
39821      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39822      */
39823     itemCls : "x-menu-item",
39824     /**
39825      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39826      */
39827     canActivate : true,
39828     /**
39829      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39830      */
39831     showDelay: 200,
39832     // doc'd in BaseItem
39833     hideDelay: 200,
39834
39835     // private
39836     ctype: "Roo.menu.Item",
39837     
39838     // private
39839     onRender : function(container, position){
39840         var el = document.createElement("a");
39841         el.hideFocus = true;
39842         el.unselectable = "on";
39843         el.href = this.href || "#";
39844         if(this.hrefTarget){
39845             el.target = this.hrefTarget;
39846         }
39847         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39848         
39849         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39850         
39851         el.innerHTML = String.format(
39852                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39853                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39854         this.el = el;
39855         Roo.menu.Item.superclass.onRender.call(this, container, position);
39856     },
39857
39858     /**
39859      * Sets the text to display in this menu item
39860      * @param {String} text The text to display
39861      * @param {Boolean} isHTML true to indicate text is pure html.
39862      */
39863     setText : function(text, isHTML){
39864         if (isHTML) {
39865             this.html = text;
39866         } else {
39867             this.text = text;
39868             this.html = '';
39869         }
39870         if(this.rendered){
39871             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39872      
39873             this.el.update(String.format(
39874                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39875                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39876             this.parentMenu.autoWidth();
39877         }
39878     },
39879
39880     // private
39881     handleClick : function(e){
39882         if(!this.href){ // if no link defined, stop the event automatically
39883             e.stopEvent();
39884         }
39885         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39886     },
39887
39888     // private
39889     activate : function(autoExpand){
39890         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39891             this.focus();
39892             if(autoExpand){
39893                 this.expandMenu();
39894             }
39895         }
39896         return true;
39897     },
39898
39899     // private
39900     shouldDeactivate : function(e){
39901         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39902             if(this.menu && this.menu.isVisible()){
39903                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39904             }
39905             return true;
39906         }
39907         return false;
39908     },
39909
39910     // private
39911     deactivate : function(){
39912         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39913         this.hideMenu();
39914     },
39915
39916     // private
39917     expandMenu : function(autoActivate){
39918         if(!this.disabled && this.menu){
39919             clearTimeout(this.hideTimer);
39920             delete this.hideTimer;
39921             if(!this.menu.isVisible() && !this.showTimer){
39922                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39923             }else if (this.menu.isVisible() && autoActivate){
39924                 this.menu.tryActivate(0, 1);
39925             }
39926         }
39927     },
39928
39929     // private
39930     deferExpand : function(autoActivate){
39931         delete this.showTimer;
39932         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39933         if(autoActivate){
39934             this.menu.tryActivate(0, 1);
39935         }
39936     },
39937
39938     // private
39939     hideMenu : function(){
39940         clearTimeout(this.showTimer);
39941         delete this.showTimer;
39942         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39943             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39944         }
39945     },
39946
39947     // private
39948     deferHide : function(){
39949         delete this.hideTimer;
39950         this.menu.hide();
39951     }
39952 });/*
39953  * Based on:
39954  * Ext JS Library 1.1.1
39955  * Copyright(c) 2006-2007, Ext JS, LLC.
39956  *
39957  * Originally Released Under LGPL - original licence link has changed is not relivant.
39958  *
39959  * Fork - LGPL
39960  * <script type="text/javascript">
39961  */
39962  
39963 /**
39964  * @class Roo.menu.CheckItem
39965  * @extends Roo.menu.Item
39966  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39967  * @constructor
39968  * Creates a new CheckItem
39969  * @param {Object} config Configuration options
39970  */
39971 Roo.menu.CheckItem = function(config){
39972     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39973     this.addEvents({
39974         /**
39975          * @event beforecheckchange
39976          * Fires before the checked value is set, providing an opportunity to cancel if needed
39977          * @param {Roo.menu.CheckItem} this
39978          * @param {Boolean} checked The new checked value that will be set
39979          */
39980         "beforecheckchange" : true,
39981         /**
39982          * @event checkchange
39983          * Fires after the checked value has been set
39984          * @param {Roo.menu.CheckItem} this
39985          * @param {Boolean} checked The checked value that was set
39986          */
39987         "checkchange" : true
39988     });
39989     if(this.checkHandler){
39990         this.on('checkchange', this.checkHandler, this.scope);
39991     }
39992 };
39993 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39994     /**
39995      * @cfg {String} group
39996      * All check items with the same group name will automatically be grouped into a single-select
39997      * radio button group (defaults to '')
39998      */
39999     /**
40000      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
40001      */
40002     itemCls : "x-menu-item x-menu-check-item",
40003     /**
40004      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
40005      */
40006     groupClass : "x-menu-group-item",
40007
40008     /**
40009      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
40010      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
40011      * initialized with checked = true will be rendered as checked.
40012      */
40013     checked: false,
40014
40015     // private
40016     ctype: "Roo.menu.CheckItem",
40017
40018     // private
40019     onRender : function(c){
40020         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
40021         if(this.group){
40022             this.el.addClass(this.groupClass);
40023         }
40024         Roo.menu.MenuMgr.registerCheckable(this);
40025         if(this.checked){
40026             this.checked = false;
40027             this.setChecked(true, true);
40028         }
40029     },
40030
40031     // private
40032     destroy : function(){
40033         if(this.rendered){
40034             Roo.menu.MenuMgr.unregisterCheckable(this);
40035         }
40036         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
40037     },
40038
40039     /**
40040      * Set the checked state of this item
40041      * @param {Boolean} checked The new checked value
40042      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
40043      */
40044     setChecked : function(state, suppressEvent){
40045         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
40046             if(this.container){
40047                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
40048             }
40049             this.checked = state;
40050             if(suppressEvent !== true){
40051                 this.fireEvent("checkchange", this, state);
40052             }
40053         }
40054     },
40055
40056     // private
40057     handleClick : function(e){
40058        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
40059            this.setChecked(!this.checked);
40060        }
40061        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
40062     }
40063 });/*
40064  * Based on:
40065  * Ext JS Library 1.1.1
40066  * Copyright(c) 2006-2007, Ext JS, LLC.
40067  *
40068  * Originally Released Under LGPL - original licence link has changed is not relivant.
40069  *
40070  * Fork - LGPL
40071  * <script type="text/javascript">
40072  */
40073  
40074 /**
40075  * @class Roo.menu.DateItem
40076  * @extends Roo.menu.Adapter
40077  * A menu item that wraps the {@link Roo.DatPicker} component.
40078  * @constructor
40079  * Creates a new DateItem
40080  * @param {Object} config Configuration options
40081  */
40082 Roo.menu.DateItem = function(config){
40083     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
40084     /** The Roo.DatePicker object @type Roo.DatePicker */
40085     this.picker = this.component;
40086     this.addEvents({select: true});
40087     
40088     this.picker.on("render", function(picker){
40089         picker.getEl().swallowEvent("click");
40090         picker.container.addClass("x-menu-date-item");
40091     });
40092
40093     this.picker.on("select", this.onSelect, this);
40094 };
40095
40096 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
40097     // private
40098     onSelect : function(picker, date){
40099         this.fireEvent("select", this, date, picker);
40100         Roo.menu.DateItem.superclass.handleClick.call(this);
40101     }
40102 });/*
40103  * Based on:
40104  * Ext JS Library 1.1.1
40105  * Copyright(c) 2006-2007, Ext JS, LLC.
40106  *
40107  * Originally Released Under LGPL - original licence link has changed is not relivant.
40108  *
40109  * Fork - LGPL
40110  * <script type="text/javascript">
40111  */
40112  
40113 /**
40114  * @class Roo.menu.ColorItem
40115  * @extends Roo.menu.Adapter
40116  * A menu item that wraps the {@link Roo.ColorPalette} component.
40117  * @constructor
40118  * Creates a new ColorItem
40119  * @param {Object} config Configuration options
40120  */
40121 Roo.menu.ColorItem = function(config){
40122     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
40123     /** The Roo.ColorPalette object @type Roo.ColorPalette */
40124     this.palette = this.component;
40125     this.relayEvents(this.palette, ["select"]);
40126     if(this.selectHandler){
40127         this.on('select', this.selectHandler, this.scope);
40128     }
40129 };
40130 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
40131  * Based on:
40132  * Ext JS Library 1.1.1
40133  * Copyright(c) 2006-2007, Ext JS, LLC.
40134  *
40135  * Originally Released Under LGPL - original licence link has changed is not relivant.
40136  *
40137  * Fork - LGPL
40138  * <script type="text/javascript">
40139  */
40140  
40141
40142 /**
40143  * @class Roo.menu.DateMenu
40144  * @extends Roo.menu.Menu
40145  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
40146  * @constructor
40147  * Creates a new DateMenu
40148  * @param {Object} config Configuration options
40149  */
40150 Roo.menu.DateMenu = function(config){
40151     Roo.menu.DateMenu.superclass.constructor.call(this, config);
40152     this.plain = true;
40153     var di = new Roo.menu.DateItem(config);
40154     this.add(di);
40155     /**
40156      * The {@link Roo.DatePicker} instance for this DateMenu
40157      * @type DatePicker
40158      */
40159     this.picker = di.picker;
40160     /**
40161      * @event select
40162      * @param {DatePicker} picker
40163      * @param {Date} date
40164      */
40165     this.relayEvents(di, ["select"]);
40166     this.on('beforeshow', function(){
40167         if(this.picker){
40168             this.picker.hideMonthPicker(false);
40169         }
40170     }, this);
40171 };
40172 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
40173     cls:'x-date-menu'
40174 });/*
40175  * Based on:
40176  * Ext JS Library 1.1.1
40177  * Copyright(c) 2006-2007, Ext JS, LLC.
40178  *
40179  * Originally Released Under LGPL - original licence link has changed is not relivant.
40180  *
40181  * Fork - LGPL
40182  * <script type="text/javascript">
40183  */
40184  
40185
40186 /**
40187  * @class Roo.menu.ColorMenu
40188  * @extends Roo.menu.Menu
40189  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
40190  * @constructor
40191  * Creates a new ColorMenu
40192  * @param {Object} config Configuration options
40193  */
40194 Roo.menu.ColorMenu = function(config){
40195     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
40196     this.plain = true;
40197     var ci = new Roo.menu.ColorItem(config);
40198     this.add(ci);
40199     /**
40200      * The {@link Roo.ColorPalette} instance for this ColorMenu
40201      * @type ColorPalette
40202      */
40203     this.palette = ci.palette;
40204     /**
40205      * @event select
40206      * @param {ColorPalette} palette
40207      * @param {String} color
40208      */
40209     this.relayEvents(ci, ["select"]);
40210 };
40211 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
40212  * Based on:
40213  * Ext JS Library 1.1.1
40214  * Copyright(c) 2006-2007, Ext JS, LLC.
40215  *
40216  * Originally Released Under LGPL - original licence link has changed is not relivant.
40217  *
40218  * Fork - LGPL
40219  * <script type="text/javascript">
40220  */
40221  
40222 /**
40223  * @class Roo.form.TextItem
40224  * @extends Roo.BoxComponent
40225  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40226  * @constructor
40227  * Creates a new TextItem
40228  * @param {Object} config Configuration options
40229  */
40230 Roo.form.TextItem = function(config){
40231     Roo.form.TextItem.superclass.constructor.call(this, config);
40232 };
40233
40234 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
40235     
40236     /**
40237      * @cfg {String} tag the tag for this item (default div)
40238      */
40239     tag : 'div',
40240     /**
40241      * @cfg {String} html the content for this item
40242      */
40243     html : '',
40244     
40245     getAutoCreate : function()
40246     {
40247         var cfg = {
40248             id: this.id,
40249             tag: this.tag,
40250             html: this.html,
40251             cls: 'x-form-item'
40252         };
40253         
40254         return cfg;
40255         
40256     },
40257     
40258     onRender : function(ct, position)
40259     {
40260         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
40261         
40262         if(!this.el){
40263             var cfg = this.getAutoCreate();
40264             if(!cfg.name){
40265                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40266             }
40267             if (!cfg.name.length) {
40268                 delete cfg.name;
40269             }
40270             this.el = ct.createChild(cfg, position);
40271         }
40272     },
40273     /*
40274      * setHTML
40275      * @param {String} html update the Contents of the element.
40276      */
40277     setHTML : function(html)
40278     {
40279         this.fieldEl.dom.innerHTML = html;
40280     }
40281     
40282 });/*
40283  * Based on:
40284  * Ext JS Library 1.1.1
40285  * Copyright(c) 2006-2007, Ext JS, LLC.
40286  *
40287  * Originally Released Under LGPL - original licence link has changed is not relivant.
40288  *
40289  * Fork - LGPL
40290  * <script type="text/javascript">
40291  */
40292  
40293 /**
40294  * @class Roo.form.Field
40295  * @extends Roo.BoxComponent
40296  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
40297  * @constructor
40298  * Creates a new Field
40299  * @param {Object} config Configuration options
40300  */
40301 Roo.form.Field = function(config){
40302     Roo.form.Field.superclass.constructor.call(this, config);
40303 };
40304
40305 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
40306     /**
40307      * @cfg {String} fieldLabel Label to use when rendering a form.
40308      */
40309        /**
40310      * @cfg {String} qtip Mouse over tip
40311      */
40312      
40313     /**
40314      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
40315      */
40316     invalidClass : "x-form-invalid",
40317     /**
40318      * @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")
40319      */
40320     invalidText : "The value in this field is invalid",
40321     /**
40322      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
40323      */
40324     focusClass : "x-form-focus",
40325     /**
40326      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
40327       automatic validation (defaults to "keyup").
40328      */
40329     validationEvent : "keyup",
40330     /**
40331      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
40332      */
40333     validateOnBlur : true,
40334     /**
40335      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
40336      */
40337     validationDelay : 250,
40338     /**
40339      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40340      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
40341      */
40342     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
40343     /**
40344      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
40345      */
40346     fieldClass : "x-form-field",
40347     /**
40348      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40349      *<pre>
40350 Value         Description
40351 -----------   ----------------------------------------------------------------------
40352 qtip          Display a quick tip when the user hovers over the field
40353 title         Display a default browser title attribute popup
40354 under         Add a block div beneath the field containing the error text
40355 side          Add an error icon to the right of the field with a popup on hover
40356 [element id]  Add the error text directly to the innerHTML of the specified element
40357 </pre>
40358      */
40359     msgTarget : 'qtip',
40360     /**
40361      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40362      */
40363     msgFx : 'normal',
40364
40365     /**
40366      * @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.
40367      */
40368     readOnly : false,
40369
40370     /**
40371      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40372      */
40373     disabled : false,
40374
40375     /**
40376      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40377      */
40378     inputType : undefined,
40379     
40380     /**
40381      * @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).
40382          */
40383         tabIndex : undefined,
40384         
40385     // private
40386     isFormField : true,
40387
40388     // private
40389     hasFocus : false,
40390     /**
40391      * @property {Roo.Element} fieldEl
40392      * Element Containing the rendered Field (with label etc.)
40393      */
40394     /**
40395      * @cfg {Mixed} value A value to initialize this field with.
40396      */
40397     value : undefined,
40398
40399     /**
40400      * @cfg {String} name The field's HTML name attribute.
40401      */
40402     /**
40403      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40404      */
40405     // private
40406     loadedValue : false,
40407      
40408      
40409         // private ??
40410         initComponent : function(){
40411         Roo.form.Field.superclass.initComponent.call(this);
40412         this.addEvents({
40413             /**
40414              * @event focus
40415              * Fires when this field receives input focus.
40416              * @param {Roo.form.Field} this
40417              */
40418             focus : true,
40419             /**
40420              * @event blur
40421              * Fires when this field loses input focus.
40422              * @param {Roo.form.Field} this
40423              */
40424             blur : true,
40425             /**
40426              * @event specialkey
40427              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40428              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40429              * @param {Roo.form.Field} this
40430              * @param {Roo.EventObject} e The event object
40431              */
40432             specialkey : true,
40433             /**
40434              * @event change
40435              * Fires just before the field blurs if the field value has changed.
40436              * @param {Roo.form.Field} this
40437              * @param {Mixed} newValue The new value
40438              * @param {Mixed} oldValue The original value
40439              */
40440             change : true,
40441             /**
40442              * @event invalid
40443              * Fires after the field has been marked as invalid.
40444              * @param {Roo.form.Field} this
40445              * @param {String} msg The validation message
40446              */
40447             invalid : true,
40448             /**
40449              * @event valid
40450              * Fires after the field has been validated with no errors.
40451              * @param {Roo.form.Field} this
40452              */
40453             valid : true,
40454              /**
40455              * @event keyup
40456              * Fires after the key up
40457              * @param {Roo.form.Field} this
40458              * @param {Roo.EventObject}  e The event Object
40459              */
40460             keyup : true
40461         });
40462     },
40463
40464     /**
40465      * Returns the name attribute of the field if available
40466      * @return {String} name The field name
40467      */
40468     getName: function(){
40469          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40470     },
40471
40472     // private
40473     onRender : function(ct, position){
40474         Roo.form.Field.superclass.onRender.call(this, ct, position);
40475         if(!this.el){
40476             var cfg = this.getAutoCreate();
40477             if(!cfg.name){
40478                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40479             }
40480             if (!cfg.name.length) {
40481                 delete cfg.name;
40482             }
40483             if(this.inputType){
40484                 cfg.type = this.inputType;
40485             }
40486             this.el = ct.createChild(cfg, position);
40487         }
40488         var type = this.el.dom.type;
40489         if(type){
40490             if(type == 'password'){
40491                 type = 'text';
40492             }
40493             this.el.addClass('x-form-'+type);
40494         }
40495         if(this.readOnly){
40496             this.el.dom.readOnly = true;
40497         }
40498         if(this.tabIndex !== undefined){
40499             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40500         }
40501
40502         this.el.addClass([this.fieldClass, this.cls]);
40503         this.initValue();
40504     },
40505
40506     /**
40507      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40508      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40509      * @return {Roo.form.Field} this
40510      */
40511     applyTo : function(target){
40512         this.allowDomMove = false;
40513         this.el = Roo.get(target);
40514         this.render(this.el.dom.parentNode);
40515         return this;
40516     },
40517
40518     // private
40519     initValue : function(){
40520         if(this.value !== undefined){
40521             this.setValue(this.value);
40522         }else if(this.el.dom.value.length > 0){
40523             this.setValue(this.el.dom.value);
40524         }
40525     },
40526
40527     /**
40528      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40529      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40530      */
40531     isDirty : function() {
40532         if(this.disabled) {
40533             return false;
40534         }
40535         return String(this.getValue()) !== String(this.originalValue);
40536     },
40537
40538     /**
40539      * stores the current value in loadedValue
40540      */
40541     resetHasChanged : function()
40542     {
40543         this.loadedValue = String(this.getValue());
40544     },
40545     /**
40546      * checks the current value against the 'loaded' value.
40547      * Note - will return false if 'resetHasChanged' has not been called first.
40548      */
40549     hasChanged : function()
40550     {
40551         if(this.disabled || this.readOnly) {
40552             return false;
40553         }
40554         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40555     },
40556     
40557     
40558     
40559     // private
40560     afterRender : function(){
40561         Roo.form.Field.superclass.afterRender.call(this);
40562         this.initEvents();
40563     },
40564
40565     // private
40566     fireKey : function(e){
40567         //Roo.log('field ' + e.getKey());
40568         if(e.isNavKeyPress()){
40569             this.fireEvent("specialkey", this, e);
40570         }
40571     },
40572
40573     /**
40574      * Resets the current field value to the originally loaded value and clears any validation messages
40575      */
40576     reset : function(){
40577         this.setValue(this.resetValue);
40578         this.originalValue = this.getValue();
40579         this.clearInvalid();
40580     },
40581
40582     // private
40583     initEvents : function(){
40584         // safari killled keypress - so keydown is now used..
40585         this.el.on("keydown" , this.fireKey,  this);
40586         this.el.on("focus", this.onFocus,  this);
40587         this.el.on("blur", this.onBlur,  this);
40588         this.el.relayEvent('keyup', this);
40589
40590         // reference to original value for reset
40591         this.originalValue = this.getValue();
40592         this.resetValue =  this.getValue();
40593     },
40594
40595     // private
40596     onFocus : function(){
40597         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40598             this.el.addClass(this.focusClass);
40599         }
40600         if(!this.hasFocus){
40601             this.hasFocus = true;
40602             this.startValue = this.getValue();
40603             this.fireEvent("focus", this);
40604         }
40605     },
40606
40607     beforeBlur : Roo.emptyFn,
40608
40609     // private
40610     onBlur : function(){
40611         this.beforeBlur();
40612         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40613             this.el.removeClass(this.focusClass);
40614         }
40615         this.hasFocus = false;
40616         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40617             this.validate();
40618         }
40619         var v = this.getValue();
40620         if(String(v) !== String(this.startValue)){
40621             this.fireEvent('change', this, v, this.startValue);
40622         }
40623         this.fireEvent("blur", this);
40624     },
40625
40626     /**
40627      * Returns whether or not the field value is currently valid
40628      * @param {Boolean} preventMark True to disable marking the field invalid
40629      * @return {Boolean} True if the value is valid, else false
40630      */
40631     isValid : function(preventMark){
40632         if(this.disabled){
40633             return true;
40634         }
40635         var restore = this.preventMark;
40636         this.preventMark = preventMark === true;
40637         var v = this.validateValue(this.processValue(this.getRawValue()));
40638         this.preventMark = restore;
40639         return v;
40640     },
40641
40642     /**
40643      * Validates the field value
40644      * @return {Boolean} True if the value is valid, else false
40645      */
40646     validate : function(){
40647         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40648             this.clearInvalid();
40649             return true;
40650         }
40651         return false;
40652     },
40653
40654     processValue : function(value){
40655         return value;
40656     },
40657
40658     // private
40659     // Subclasses should provide the validation implementation by overriding this
40660     validateValue : function(value){
40661         return true;
40662     },
40663
40664     /**
40665      * Mark this field as invalid
40666      * @param {String} msg The validation message
40667      */
40668     markInvalid : function(msg){
40669         if(!this.rendered || this.preventMark){ // not rendered
40670             return;
40671         }
40672         
40673         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40674         
40675         obj.el.addClass(this.invalidClass);
40676         msg = msg || this.invalidText;
40677         switch(this.msgTarget){
40678             case 'qtip':
40679                 obj.el.dom.qtip = msg;
40680                 obj.el.dom.qclass = 'x-form-invalid-tip';
40681                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40682                     Roo.QuickTips.enable();
40683                 }
40684                 break;
40685             case 'title':
40686                 this.el.dom.title = msg;
40687                 break;
40688             case 'under':
40689                 if(!this.errorEl){
40690                     var elp = this.el.findParent('.x-form-element', 5, true);
40691                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40692                     this.errorEl.setWidth(elp.getWidth(true)-20);
40693                 }
40694                 this.errorEl.update(msg);
40695                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40696                 break;
40697             case 'side':
40698                 if(!this.errorIcon){
40699                     var elp = this.el.findParent('.x-form-element', 5, true);
40700                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40701                 }
40702                 this.alignErrorIcon();
40703                 this.errorIcon.dom.qtip = msg;
40704                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40705                 this.errorIcon.show();
40706                 this.on('resize', this.alignErrorIcon, this);
40707                 break;
40708             default:
40709                 var t = Roo.getDom(this.msgTarget);
40710                 t.innerHTML = msg;
40711                 t.style.display = this.msgDisplay;
40712                 break;
40713         }
40714         this.fireEvent('invalid', this, msg);
40715     },
40716
40717     // private
40718     alignErrorIcon : function(){
40719         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40720     },
40721
40722     /**
40723      * Clear any invalid styles/messages for this field
40724      */
40725     clearInvalid : function(){
40726         if(!this.rendered || this.preventMark){ // not rendered
40727             return;
40728         }
40729         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40730         
40731         obj.el.removeClass(this.invalidClass);
40732         switch(this.msgTarget){
40733             case 'qtip':
40734                 obj.el.dom.qtip = '';
40735                 break;
40736             case 'title':
40737                 this.el.dom.title = '';
40738                 break;
40739             case 'under':
40740                 if(this.errorEl){
40741                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40742                 }
40743                 break;
40744             case 'side':
40745                 if(this.errorIcon){
40746                     this.errorIcon.dom.qtip = '';
40747                     this.errorIcon.hide();
40748                     this.un('resize', this.alignErrorIcon, this);
40749                 }
40750                 break;
40751             default:
40752                 var t = Roo.getDom(this.msgTarget);
40753                 t.innerHTML = '';
40754                 t.style.display = 'none';
40755                 break;
40756         }
40757         this.fireEvent('valid', this);
40758     },
40759
40760     /**
40761      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40762      * @return {Mixed} value The field value
40763      */
40764     getRawValue : function(){
40765         var v = this.el.getValue();
40766         
40767         return v;
40768     },
40769
40770     /**
40771      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40772      * @return {Mixed} value The field value
40773      */
40774     getValue : function(){
40775         var v = this.el.getValue();
40776          
40777         return v;
40778     },
40779
40780     /**
40781      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40782      * @param {Mixed} value The value to set
40783      */
40784     setRawValue : function(v){
40785         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40786     },
40787
40788     /**
40789      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40790      * @param {Mixed} value The value to set
40791      */
40792     setValue : function(v){
40793         this.value = v;
40794         if(this.rendered){
40795             this.el.dom.value = (v === null || v === undefined ? '' : v);
40796              this.validate();
40797         }
40798     },
40799
40800     adjustSize : function(w, h){
40801         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40802         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40803         return s;
40804     },
40805
40806     adjustWidth : function(tag, w){
40807         tag = tag.toLowerCase();
40808         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40809             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40810                 if(tag == 'input'){
40811                     return w + 2;
40812                 }
40813                 if(tag == 'textarea'){
40814                     return w-2;
40815                 }
40816             }else if(Roo.isOpera){
40817                 if(tag == 'input'){
40818                     return w + 2;
40819                 }
40820                 if(tag == 'textarea'){
40821                     return w-2;
40822                 }
40823             }
40824         }
40825         return w;
40826     }
40827 });
40828
40829
40830 // anything other than normal should be considered experimental
40831 Roo.form.Field.msgFx = {
40832     normal : {
40833         show: function(msgEl, f){
40834             msgEl.setDisplayed('block');
40835         },
40836
40837         hide : function(msgEl, f){
40838             msgEl.setDisplayed(false).update('');
40839         }
40840     },
40841
40842     slide : {
40843         show: function(msgEl, f){
40844             msgEl.slideIn('t', {stopFx:true});
40845         },
40846
40847         hide : function(msgEl, f){
40848             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40849         }
40850     },
40851
40852     slideRight : {
40853         show: function(msgEl, f){
40854             msgEl.fixDisplay();
40855             msgEl.alignTo(f.el, 'tl-tr');
40856             msgEl.slideIn('l', {stopFx:true});
40857         },
40858
40859         hide : function(msgEl, f){
40860             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40861         }
40862     }
40863 };/*
40864  * Based on:
40865  * Ext JS Library 1.1.1
40866  * Copyright(c) 2006-2007, Ext JS, LLC.
40867  *
40868  * Originally Released Under LGPL - original licence link has changed is not relivant.
40869  *
40870  * Fork - LGPL
40871  * <script type="text/javascript">
40872  */
40873  
40874
40875 /**
40876  * @class Roo.form.TextField
40877  * @extends Roo.form.Field
40878  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40879  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40880  * @constructor
40881  * Creates a new TextField
40882  * @param {Object} config Configuration options
40883  */
40884 Roo.form.TextField = function(config){
40885     Roo.form.TextField.superclass.constructor.call(this, config);
40886     this.addEvents({
40887         /**
40888          * @event autosize
40889          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40890          * according to the default logic, but this event provides a hook for the developer to apply additional
40891          * logic at runtime to resize the field if needed.
40892              * @param {Roo.form.Field} this This text field
40893              * @param {Number} width The new field width
40894              */
40895         autosize : true
40896     });
40897 };
40898
40899 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40900     /**
40901      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40902      */
40903     grow : false,
40904     /**
40905      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40906      */
40907     growMin : 30,
40908     /**
40909      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40910      */
40911     growMax : 800,
40912     /**
40913      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40914      */
40915     vtype : null,
40916     /**
40917      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40918      */
40919     maskRe : null,
40920     /**
40921      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40922      */
40923     disableKeyFilter : false,
40924     /**
40925      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40926      */
40927     allowBlank : true,
40928     /**
40929      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40930      */
40931     minLength : 0,
40932     /**
40933      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40934      */
40935     maxLength : Number.MAX_VALUE,
40936     /**
40937      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40938      */
40939     minLengthText : "The minimum length for this field is {0}",
40940     /**
40941      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40942      */
40943     maxLengthText : "The maximum length for this field is {0}",
40944     /**
40945      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40946      */
40947     selectOnFocus : false,
40948     /**
40949      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40950      */    
40951     allowLeadingSpace : false,
40952     /**
40953      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40954      */
40955     blankText : "This field is required",
40956     /**
40957      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40958      * If available, this function will be called only after the basic validators all return true, and will be passed the
40959      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40960      */
40961     validator : null,
40962     /**
40963      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40964      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40965      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40966      */
40967     regex : null,
40968     /**
40969      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40970      */
40971     regexText : "",
40972     /**
40973      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40974      */
40975     emptyText : null,
40976    
40977
40978     // private
40979     initEvents : function()
40980     {
40981         if (this.emptyText) {
40982             this.el.attr('placeholder', this.emptyText);
40983         }
40984         
40985         Roo.form.TextField.superclass.initEvents.call(this);
40986         if(this.validationEvent == 'keyup'){
40987             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40988             this.el.on('keyup', this.filterValidation, this);
40989         }
40990         else if(this.validationEvent !== false){
40991             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40992         }
40993         
40994         if(this.selectOnFocus){
40995             this.on("focus", this.preFocus, this);
40996         }
40997         if (!this.allowLeadingSpace) {
40998             this.on('blur', this.cleanLeadingSpace, this);
40999         }
41000         
41001         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
41002             this.el.on("keypress", this.filterKeys, this);
41003         }
41004         if(this.grow){
41005             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
41006             this.el.on("click", this.autoSize,  this);
41007         }
41008         if(this.el.is('input[type=password]') && Roo.isSafari){
41009             this.el.on('keydown', this.SafariOnKeyDown, this);
41010         }
41011     },
41012
41013     processValue : function(value){
41014         if(this.stripCharsRe){
41015             var newValue = value.replace(this.stripCharsRe, '');
41016             if(newValue !== value){
41017                 this.setRawValue(newValue);
41018                 return newValue;
41019             }
41020         }
41021         return value;
41022     },
41023
41024     filterValidation : function(e){
41025         if(!e.isNavKeyPress()){
41026             this.validationTask.delay(this.validationDelay);
41027         }
41028     },
41029
41030     // private
41031     onKeyUp : function(e){
41032         if(!e.isNavKeyPress()){
41033             this.autoSize();
41034         }
41035     },
41036     // private - clean the leading white space
41037     cleanLeadingSpace : function(e)
41038     {
41039         if ( this.inputType == 'file') {
41040             return;
41041         }
41042         
41043         this.setValue((this.getValue() + '').replace(/^\s+/,''));
41044     },
41045     /**
41046      * Resets the current field value to the originally-loaded value and clears any validation messages.
41047      *  
41048      */
41049     reset : function(){
41050         Roo.form.TextField.superclass.reset.call(this);
41051        
41052     }, 
41053     // private
41054     preFocus : function(){
41055         
41056         if(this.selectOnFocus){
41057             this.el.dom.select();
41058         }
41059     },
41060
41061     
41062     // private
41063     filterKeys : function(e){
41064         var k = e.getKey();
41065         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
41066             return;
41067         }
41068         var c = e.getCharCode(), cc = String.fromCharCode(c);
41069         if(Roo.isIE && (e.isSpecialKey() || !cc)){
41070             return;
41071         }
41072         if(!this.maskRe.test(cc)){
41073             e.stopEvent();
41074         }
41075     },
41076
41077     setValue : function(v){
41078         
41079         Roo.form.TextField.superclass.setValue.apply(this, arguments);
41080         
41081         this.autoSize();
41082     },
41083
41084     /**
41085      * Validates a value according to the field's validation rules and marks the field as invalid
41086      * if the validation fails
41087      * @param {Mixed} value The value to validate
41088      * @return {Boolean} True if the value is valid, else false
41089      */
41090     validateValue : function(value){
41091         if(value.length < 1)  { // if it's blank
41092              if(this.allowBlank){
41093                 this.clearInvalid();
41094                 return true;
41095              }else{
41096                 this.markInvalid(this.blankText);
41097                 return false;
41098              }
41099         }
41100         if(value.length < this.minLength){
41101             this.markInvalid(String.format(this.minLengthText, this.minLength));
41102             return false;
41103         }
41104         if(value.length > this.maxLength){
41105             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
41106             return false;
41107         }
41108         if(this.vtype){
41109             var vt = Roo.form.VTypes;
41110             if(!vt[this.vtype](value, this)){
41111                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
41112                 return false;
41113             }
41114         }
41115         if(typeof this.validator == "function"){
41116             var msg = this.validator(value);
41117             if(msg !== true){
41118                 this.markInvalid(msg);
41119                 return false;
41120             }
41121         }
41122         if(this.regex && !this.regex.test(value)){
41123             this.markInvalid(this.regexText);
41124             return false;
41125         }
41126         return true;
41127     },
41128
41129     /**
41130      * Selects text in this field
41131      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
41132      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
41133      */
41134     selectText : function(start, end){
41135         var v = this.getRawValue();
41136         if(v.length > 0){
41137             start = start === undefined ? 0 : start;
41138             end = end === undefined ? v.length : end;
41139             var d = this.el.dom;
41140             if(d.setSelectionRange){
41141                 d.setSelectionRange(start, end);
41142             }else if(d.createTextRange){
41143                 var range = d.createTextRange();
41144                 range.moveStart("character", start);
41145                 range.moveEnd("character", v.length-end);
41146                 range.select();
41147             }
41148         }
41149     },
41150
41151     /**
41152      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
41153      * This only takes effect if grow = true, and fires the autosize event.
41154      */
41155     autoSize : function(){
41156         if(!this.grow || !this.rendered){
41157             return;
41158         }
41159         if(!this.metrics){
41160             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
41161         }
41162         var el = this.el;
41163         var v = el.dom.value;
41164         var d = document.createElement('div');
41165         d.appendChild(document.createTextNode(v));
41166         v = d.innerHTML;
41167         d = null;
41168         v += "&#160;";
41169         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
41170         this.el.setWidth(w);
41171         this.fireEvent("autosize", this, w);
41172     },
41173     
41174     // private
41175     SafariOnKeyDown : function(event)
41176     {
41177         // this is a workaround for a password hang bug on chrome/ webkit.
41178         
41179         var isSelectAll = false;
41180         
41181         if(this.el.dom.selectionEnd > 0){
41182             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
41183         }
41184         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
41185             event.preventDefault();
41186             this.setValue('');
41187             return;
41188         }
41189         
41190         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
41191             
41192             event.preventDefault();
41193             // this is very hacky as keydown always get's upper case.
41194             
41195             var cc = String.fromCharCode(event.getCharCode());
41196             
41197             
41198             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
41199             
41200         }
41201         
41202         
41203     }
41204 });/*
41205  * Based on:
41206  * Ext JS Library 1.1.1
41207  * Copyright(c) 2006-2007, Ext JS, LLC.
41208  *
41209  * Originally Released Under LGPL - original licence link has changed is not relivant.
41210  *
41211  * Fork - LGPL
41212  * <script type="text/javascript">
41213  */
41214  
41215 /**
41216  * @class Roo.form.Hidden
41217  * @extends Roo.form.TextField
41218  * Simple Hidden element used on forms 
41219  * 
41220  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
41221  * 
41222  * @constructor
41223  * Creates a new Hidden form element.
41224  * @param {Object} config Configuration options
41225  */
41226
41227
41228
41229 // easy hidden field...
41230 Roo.form.Hidden = function(config){
41231     Roo.form.Hidden.superclass.constructor.call(this, config);
41232 };
41233   
41234 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
41235     fieldLabel:      '',
41236     inputType:      'hidden',
41237     width:          50,
41238     allowBlank:     true,
41239     labelSeparator: '',
41240     hidden:         true,
41241     itemCls :       'x-form-item-display-none'
41242
41243
41244 });
41245
41246
41247 /*
41248  * Based on:
41249  * Ext JS Library 1.1.1
41250  * Copyright(c) 2006-2007, Ext JS, LLC.
41251  *
41252  * Originally Released Under LGPL - original licence link has changed is not relivant.
41253  *
41254  * Fork - LGPL
41255  * <script type="text/javascript">
41256  */
41257  
41258 /**
41259  * @class Roo.form.TriggerField
41260  * @extends Roo.form.TextField
41261  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
41262  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
41263  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
41264  * for which you can provide a custom implementation.  For example:
41265  * <pre><code>
41266 var trigger = new Roo.form.TriggerField();
41267 trigger.onTriggerClick = myTriggerFn;
41268 trigger.applyTo('my-field');
41269 </code></pre>
41270  *
41271  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
41272  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
41273  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41274  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
41275  * @constructor
41276  * Create a new TriggerField.
41277  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
41278  * to the base TextField)
41279  */
41280 Roo.form.TriggerField = function(config){
41281     this.mimicing = false;
41282     Roo.form.TriggerField.superclass.constructor.call(this, config);
41283 };
41284
41285 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
41286     /**
41287      * @cfg {String} triggerClass A CSS class to apply to the trigger
41288      */
41289     /**
41290      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41291      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
41292      */
41293     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
41294     /**
41295      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
41296      */
41297     hideTrigger:false,
41298
41299     /** @cfg {Boolean} grow @hide */
41300     /** @cfg {Number} growMin @hide */
41301     /** @cfg {Number} growMax @hide */
41302
41303     /**
41304      * @hide 
41305      * @method
41306      */
41307     autoSize: Roo.emptyFn,
41308     // private
41309     monitorTab : true,
41310     // private
41311     deferHeight : true,
41312
41313     
41314     actionMode : 'wrap',
41315     // private
41316     onResize : function(w, h){
41317         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
41318         if(typeof w == 'number'){
41319             var x = w - this.trigger.getWidth();
41320             this.el.setWidth(this.adjustWidth('input', x));
41321             this.trigger.setStyle('left', x+'px');
41322         }
41323     },
41324
41325     // private
41326     adjustSize : Roo.BoxComponent.prototype.adjustSize,
41327
41328     // private
41329     getResizeEl : function(){
41330         return this.wrap;
41331     },
41332
41333     // private
41334     getPositionEl : function(){
41335         return this.wrap;
41336     },
41337
41338     // private
41339     alignErrorIcon : function(){
41340         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
41341     },
41342
41343     // private
41344     onRender : function(ct, position){
41345         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
41346         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41347         this.trigger = this.wrap.createChild(this.triggerConfig ||
41348                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41349         if(this.hideTrigger){
41350             this.trigger.setDisplayed(false);
41351         }
41352         this.initTrigger();
41353         if(!this.width){
41354             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41355         }
41356     },
41357
41358     // private
41359     initTrigger : function(){
41360         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41361         this.trigger.addClassOnOver('x-form-trigger-over');
41362         this.trigger.addClassOnClick('x-form-trigger-click');
41363     },
41364
41365     // private
41366     onDestroy : function(){
41367         if(this.trigger){
41368             this.trigger.removeAllListeners();
41369             this.trigger.remove();
41370         }
41371         if(this.wrap){
41372             this.wrap.remove();
41373         }
41374         Roo.form.TriggerField.superclass.onDestroy.call(this);
41375     },
41376
41377     // private
41378     onFocus : function(){
41379         Roo.form.TriggerField.superclass.onFocus.call(this);
41380         if(!this.mimicing){
41381             this.wrap.addClass('x-trigger-wrap-focus');
41382             this.mimicing = true;
41383             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41384             if(this.monitorTab){
41385                 this.el.on("keydown", this.checkTab, this);
41386             }
41387         }
41388     },
41389
41390     // private
41391     checkTab : function(e){
41392         if(e.getKey() == e.TAB){
41393             this.triggerBlur();
41394         }
41395     },
41396
41397     // private
41398     onBlur : function(){
41399         // do nothing
41400     },
41401
41402     // private
41403     mimicBlur : function(e, t){
41404         if(!this.wrap.contains(t) && this.validateBlur()){
41405             this.triggerBlur();
41406         }
41407     },
41408
41409     // private
41410     triggerBlur : function(){
41411         this.mimicing = false;
41412         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41413         if(this.monitorTab){
41414             this.el.un("keydown", this.checkTab, this);
41415         }
41416         this.wrap.removeClass('x-trigger-wrap-focus');
41417         Roo.form.TriggerField.superclass.onBlur.call(this);
41418     },
41419
41420     // private
41421     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41422     validateBlur : function(e, t){
41423         return true;
41424     },
41425
41426     // private
41427     onDisable : function(){
41428         Roo.form.TriggerField.superclass.onDisable.call(this);
41429         if(this.wrap){
41430             this.wrap.addClass('x-item-disabled');
41431         }
41432     },
41433
41434     // private
41435     onEnable : function(){
41436         Roo.form.TriggerField.superclass.onEnable.call(this);
41437         if(this.wrap){
41438             this.wrap.removeClass('x-item-disabled');
41439         }
41440     },
41441
41442     // private
41443     onShow : function(){
41444         var ae = this.getActionEl();
41445         
41446         if(ae){
41447             ae.dom.style.display = '';
41448             ae.dom.style.visibility = 'visible';
41449         }
41450     },
41451
41452     // private
41453     
41454     onHide : function(){
41455         var ae = this.getActionEl();
41456         ae.dom.style.display = 'none';
41457     },
41458
41459     /**
41460      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41461      * by an implementing function.
41462      * @method
41463      * @param {EventObject} e
41464      */
41465     onTriggerClick : Roo.emptyFn
41466 });
41467
41468 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41469 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41470 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41471 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41472     initComponent : function(){
41473         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41474
41475         this.triggerConfig = {
41476             tag:'span', cls:'x-form-twin-triggers', cn:[
41477             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41478             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41479         ]};
41480     },
41481
41482     getTrigger : function(index){
41483         return this.triggers[index];
41484     },
41485
41486     initTrigger : function(){
41487         var ts = this.trigger.select('.x-form-trigger', true);
41488         this.wrap.setStyle('overflow', 'hidden');
41489         var triggerField = this;
41490         ts.each(function(t, all, index){
41491             t.hide = function(){
41492                 var w = triggerField.wrap.getWidth();
41493                 this.dom.style.display = 'none';
41494                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41495             };
41496             t.show = function(){
41497                 var w = triggerField.wrap.getWidth();
41498                 this.dom.style.display = '';
41499                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41500             };
41501             var triggerIndex = 'Trigger'+(index+1);
41502
41503             if(this['hide'+triggerIndex]){
41504                 t.dom.style.display = 'none';
41505             }
41506             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41507             t.addClassOnOver('x-form-trigger-over');
41508             t.addClassOnClick('x-form-trigger-click');
41509         }, this);
41510         this.triggers = ts.elements;
41511     },
41512
41513     onTrigger1Click : Roo.emptyFn,
41514     onTrigger2Click : Roo.emptyFn
41515 });/*
41516  * Based on:
41517  * Ext JS Library 1.1.1
41518  * Copyright(c) 2006-2007, Ext JS, LLC.
41519  *
41520  * Originally Released Under LGPL - original licence link has changed is not relivant.
41521  *
41522  * Fork - LGPL
41523  * <script type="text/javascript">
41524  */
41525  
41526 /**
41527  * @class Roo.form.TextArea
41528  * @extends Roo.form.TextField
41529  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41530  * support for auto-sizing.
41531  * @constructor
41532  * Creates a new TextArea
41533  * @param {Object} config Configuration options
41534  */
41535 Roo.form.TextArea = function(config){
41536     Roo.form.TextArea.superclass.constructor.call(this, config);
41537     // these are provided exchanges for backwards compat
41538     // minHeight/maxHeight were replaced by growMin/growMax to be
41539     // compatible with TextField growing config values
41540     if(this.minHeight !== undefined){
41541         this.growMin = this.minHeight;
41542     }
41543     if(this.maxHeight !== undefined){
41544         this.growMax = this.maxHeight;
41545     }
41546 };
41547
41548 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41549     /**
41550      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41551      */
41552     growMin : 60,
41553     /**
41554      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41555      */
41556     growMax: 1000,
41557     /**
41558      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41559      * in the field (equivalent to setting overflow: hidden, defaults to false)
41560      */
41561     preventScrollbars: false,
41562     /**
41563      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41564      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41565      */
41566
41567     // private
41568     onRender : function(ct, position){
41569         if(!this.el){
41570             this.defaultAutoCreate = {
41571                 tag: "textarea",
41572                 style:"width:300px;height:60px;",
41573                 autocomplete: "new-password"
41574             };
41575         }
41576         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41577         if(this.grow){
41578             this.textSizeEl = Roo.DomHelper.append(document.body, {
41579                 tag: "pre", cls: "x-form-grow-sizer"
41580             });
41581             if(this.preventScrollbars){
41582                 this.el.setStyle("overflow", "hidden");
41583             }
41584             this.el.setHeight(this.growMin);
41585         }
41586     },
41587
41588     onDestroy : function(){
41589         if(this.textSizeEl){
41590             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41591         }
41592         Roo.form.TextArea.superclass.onDestroy.call(this);
41593     },
41594
41595     // private
41596     onKeyUp : function(e){
41597         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41598             this.autoSize();
41599         }
41600     },
41601
41602     /**
41603      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41604      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41605      */
41606     autoSize : function(){
41607         if(!this.grow || !this.textSizeEl){
41608             return;
41609         }
41610         var el = this.el;
41611         var v = el.dom.value;
41612         var ts = this.textSizeEl;
41613
41614         ts.innerHTML = '';
41615         ts.appendChild(document.createTextNode(v));
41616         v = ts.innerHTML;
41617
41618         Roo.fly(ts).setWidth(this.el.getWidth());
41619         if(v.length < 1){
41620             v = "&#160;&#160;";
41621         }else{
41622             if(Roo.isIE){
41623                 v = v.replace(/\n/g, '<p>&#160;</p>');
41624             }
41625             v += "&#160;\n&#160;";
41626         }
41627         ts.innerHTML = v;
41628         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41629         if(h != this.lastHeight){
41630             this.lastHeight = h;
41631             this.el.setHeight(h);
41632             this.fireEvent("autosize", this, h);
41633         }
41634     }
41635 });/*
41636  * Based on:
41637  * Ext JS Library 1.1.1
41638  * Copyright(c) 2006-2007, Ext JS, LLC.
41639  *
41640  * Originally Released Under LGPL - original licence link has changed is not relivant.
41641  *
41642  * Fork - LGPL
41643  * <script type="text/javascript">
41644  */
41645  
41646
41647 /**
41648  * @class Roo.form.NumberField
41649  * @extends Roo.form.TextField
41650  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41651  * @constructor
41652  * Creates a new NumberField
41653  * @param {Object} config Configuration options
41654  */
41655 Roo.form.NumberField = function(config){
41656     Roo.form.NumberField.superclass.constructor.call(this, config);
41657 };
41658
41659 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41660     /**
41661      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41662      */
41663     fieldClass: "x-form-field x-form-num-field",
41664     /**
41665      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41666      */
41667     allowDecimals : true,
41668     /**
41669      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41670      */
41671     decimalSeparator : ".",
41672     /**
41673      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41674      */
41675     decimalPrecision : 2,
41676     /**
41677      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41678      */
41679     allowNegative : true,
41680     /**
41681      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41682      */
41683     minValue : Number.NEGATIVE_INFINITY,
41684     /**
41685      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41686      */
41687     maxValue : Number.MAX_VALUE,
41688     /**
41689      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41690      */
41691     minText : "The minimum value for this field is {0}",
41692     /**
41693      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41694      */
41695     maxText : "The maximum value for this field is {0}",
41696     /**
41697      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41698      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41699      */
41700     nanText : "{0} is not a valid number",
41701
41702     // private
41703     initEvents : function(){
41704         Roo.form.NumberField.superclass.initEvents.call(this);
41705         var allowed = "0123456789";
41706         if(this.allowDecimals){
41707             allowed += this.decimalSeparator;
41708         }
41709         if(this.allowNegative){
41710             allowed += "-";
41711         }
41712         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41713         var keyPress = function(e){
41714             var k = e.getKey();
41715             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41716                 return;
41717             }
41718             var c = e.getCharCode();
41719             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41720                 e.stopEvent();
41721             }
41722         };
41723         this.el.on("keypress", keyPress, this);
41724     },
41725
41726     // private
41727     validateValue : function(value){
41728         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41729             return false;
41730         }
41731         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41732              return true;
41733         }
41734         var num = this.parseValue(value);
41735         if(isNaN(num)){
41736             this.markInvalid(String.format(this.nanText, value));
41737             return false;
41738         }
41739         if(num < this.minValue){
41740             this.markInvalid(String.format(this.minText, this.minValue));
41741             return false;
41742         }
41743         if(num > this.maxValue){
41744             this.markInvalid(String.format(this.maxText, this.maxValue));
41745             return false;
41746         }
41747         return true;
41748     },
41749
41750     getValue : function(){
41751         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41752     },
41753
41754     // private
41755     parseValue : function(value){
41756         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41757         return isNaN(value) ? '' : value;
41758     },
41759
41760     // private
41761     fixPrecision : function(value){
41762         var nan = isNaN(value);
41763         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41764             return nan ? '' : value;
41765         }
41766         return parseFloat(value).toFixed(this.decimalPrecision);
41767     },
41768
41769     setValue : function(v){
41770         v = this.fixPrecision(v);
41771         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41772     },
41773
41774     // private
41775     decimalPrecisionFcn : function(v){
41776         return Math.floor(v);
41777     },
41778
41779     beforeBlur : function(){
41780         var v = this.parseValue(this.getRawValue());
41781         if(v){
41782             this.setValue(v);
41783         }
41784     }
41785 });/*
41786  * Based on:
41787  * Ext JS Library 1.1.1
41788  * Copyright(c) 2006-2007, Ext JS, LLC.
41789  *
41790  * Originally Released Under LGPL - original licence link has changed is not relivant.
41791  *
41792  * Fork - LGPL
41793  * <script type="text/javascript">
41794  */
41795  
41796 /**
41797  * @class Roo.form.DateField
41798  * @extends Roo.form.TriggerField
41799  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41800 * @constructor
41801 * Create a new DateField
41802 * @param {Object} config
41803  */
41804 Roo.form.DateField = function(config)
41805 {
41806     Roo.form.DateField.superclass.constructor.call(this, config);
41807     
41808       this.addEvents({
41809          
41810         /**
41811          * @event select
41812          * Fires when a date is selected
41813              * @param {Roo.form.DateField} combo This combo box
41814              * @param {Date} date The date selected
41815              */
41816         'select' : true
41817          
41818     });
41819     
41820     
41821     if(typeof this.minValue == "string") {
41822         this.minValue = this.parseDate(this.minValue);
41823     }
41824     if(typeof this.maxValue == "string") {
41825         this.maxValue = this.parseDate(this.maxValue);
41826     }
41827     this.ddMatch = null;
41828     if(this.disabledDates){
41829         var dd = this.disabledDates;
41830         var re = "(?:";
41831         for(var i = 0; i < dd.length; i++){
41832             re += dd[i];
41833             if(i != dd.length-1) {
41834                 re += "|";
41835             }
41836         }
41837         this.ddMatch = new RegExp(re + ")");
41838     }
41839 };
41840
41841 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41842     /**
41843      * @cfg {String} format
41844      * The default date format string which can be overriden for localization support.  The format must be
41845      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41846      */
41847     format : "m/d/y",
41848     /**
41849      * @cfg {String} altFormats
41850      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41851      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41852      */
41853     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41854     /**
41855      * @cfg {Array} disabledDays
41856      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41857      */
41858     disabledDays : null,
41859     /**
41860      * @cfg {String} disabledDaysText
41861      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41862      */
41863     disabledDaysText : "Disabled",
41864     /**
41865      * @cfg {Array} disabledDates
41866      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41867      * expression so they are very powerful. Some examples:
41868      * <ul>
41869      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41870      * <li>["03/08", "09/16"] would disable those days for every year</li>
41871      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41872      * <li>["03/../2006"] would disable every day in March 2006</li>
41873      * <li>["^03"] would disable every day in every March</li>
41874      * </ul>
41875      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41876      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41877      */
41878     disabledDates : null,
41879     /**
41880      * @cfg {String} disabledDatesText
41881      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41882      */
41883     disabledDatesText : "Disabled",
41884         
41885         
41886         /**
41887      * @cfg {Date/String} zeroValue
41888      * if the date is less that this number, then the field is rendered as empty
41889      * default is 1800
41890      */
41891         zeroValue : '1800-01-01',
41892         
41893         
41894     /**
41895      * @cfg {Date/String} minValue
41896      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41897      * valid format (defaults to null).
41898      */
41899     minValue : null,
41900     /**
41901      * @cfg {Date/String} maxValue
41902      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41903      * valid format (defaults to null).
41904      */
41905     maxValue : null,
41906     /**
41907      * @cfg {String} minText
41908      * The error text to display when the date in the cell is before minValue (defaults to
41909      * 'The date in this field must be after {minValue}').
41910      */
41911     minText : "The date in this field must be equal to or after {0}",
41912     /**
41913      * @cfg {String} maxText
41914      * The error text to display when the date in the cell is after maxValue (defaults to
41915      * 'The date in this field must be before {maxValue}').
41916      */
41917     maxText : "The date in this field must be equal to or before {0}",
41918     /**
41919      * @cfg {String} invalidText
41920      * The error text to display when the date in the field is invalid (defaults to
41921      * '{value} is not a valid date - it must be in the format {format}').
41922      */
41923     invalidText : "{0} is not a valid date - it must be in the format {1}",
41924     /**
41925      * @cfg {String} triggerClass
41926      * An additional CSS class used to style the trigger button.  The trigger will always get the
41927      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41928      * which displays a calendar icon).
41929      */
41930     triggerClass : 'x-form-date-trigger',
41931     
41932
41933     /**
41934      * @cfg {Boolean} useIso
41935      * if enabled, then the date field will use a hidden field to store the 
41936      * real value as iso formated date. default (false)
41937      */ 
41938     useIso : false,
41939     /**
41940      * @cfg {String/Object} autoCreate
41941      * A DomHelper element spec, or true for a default element spec (defaults to
41942      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41943      */ 
41944     // private
41945     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41946     
41947     // private
41948     hiddenField: false,
41949     
41950     onRender : function(ct, position)
41951     {
41952         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41953         if (this.useIso) {
41954             //this.el.dom.removeAttribute('name'); 
41955             Roo.log("Changing name?");
41956             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41957             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41958                     'before', true);
41959             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41960             // prevent input submission
41961             this.hiddenName = this.name;
41962         }
41963             
41964             
41965     },
41966     
41967     // private
41968     validateValue : function(value)
41969     {
41970         value = this.formatDate(value);
41971         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41972             Roo.log('super failed');
41973             return false;
41974         }
41975         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41976              return true;
41977         }
41978         var svalue = value;
41979         value = this.parseDate(value);
41980         if(!value){
41981             Roo.log('parse date failed' + svalue);
41982             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41983             return false;
41984         }
41985         var time = value.getTime();
41986         if(this.minValue && time < this.minValue.getTime()){
41987             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41988             return false;
41989         }
41990         if(this.maxValue && time > this.maxValue.getTime()){
41991             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41992             return false;
41993         }
41994         if(this.disabledDays){
41995             var day = value.getDay();
41996             for(var i = 0; i < this.disabledDays.length; i++) {
41997                 if(day === this.disabledDays[i]){
41998                     this.markInvalid(this.disabledDaysText);
41999                     return false;
42000                 }
42001             }
42002         }
42003         var fvalue = this.formatDate(value);
42004         if(this.ddMatch && this.ddMatch.test(fvalue)){
42005             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42006             return false;
42007         }
42008         return true;
42009     },
42010
42011     // private
42012     // Provides logic to override the default TriggerField.validateBlur which just returns true
42013     validateBlur : function(){
42014         return !this.menu || !this.menu.isVisible();
42015     },
42016     
42017     getName: function()
42018     {
42019         // returns hidden if it's set..
42020         if (!this.rendered) {return ''};
42021         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42022         
42023     },
42024
42025     /**
42026      * Returns the current date value of the date field.
42027      * @return {Date} The date value
42028      */
42029     getValue : function(){
42030         
42031         return  this.hiddenField ?
42032                 this.hiddenField.value :
42033                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
42034     },
42035
42036     /**
42037      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42038      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
42039      * (the default format used is "m/d/y").
42040      * <br />Usage:
42041      * <pre><code>
42042 //All of these calls set the same date value (May 4, 2006)
42043
42044 //Pass a date object:
42045 var dt = new Date('5/4/06');
42046 dateField.setValue(dt);
42047
42048 //Pass a date string (default format):
42049 dateField.setValue('5/4/06');
42050
42051 //Pass a date string (custom format):
42052 dateField.format = 'Y-m-d';
42053 dateField.setValue('2006-5-4');
42054 </code></pre>
42055      * @param {String/Date} date The date or valid date string
42056      */
42057     setValue : function(date){
42058         if (this.hiddenField) {
42059             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42060         }
42061         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42062         // make sure the value field is always stored as a date..
42063         this.value = this.parseDate(date);
42064         
42065         
42066     },
42067
42068     // private
42069     parseDate : function(value){
42070                 
42071                 if (value instanceof Date) {
42072                         if (value < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42073                                 return  '';
42074                         }
42075                         return value;
42076                 }
42077                 
42078                 
42079         if(!value || value instanceof Date){
42080             return value;
42081         }
42082         var v = Date.parseDate(value, this.format);
42083          if (!v && this.useIso) {
42084             v = Date.parseDate(value, 'Y-m-d');
42085         }
42086         if(!v && this.altFormats){
42087             if(!this.altFormatsArray){
42088                 this.altFormatsArray = this.altFormats.split("|");
42089             }
42090             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42091                 v = Date.parseDate(value, this.altFormatsArray[i]);
42092             }
42093         }
42094                 if (v < Date.parseDate(this.zeroValue, 'Y-m-d') ) {
42095                         v = '';
42096                 }
42097         return v;
42098     },
42099
42100     // private
42101     formatDate : function(date, fmt){
42102         return (!date || !(date instanceof Date)) ?
42103                date : date.dateFormat(fmt || this.format);
42104     },
42105
42106     // private
42107     menuListeners : {
42108         select: function(m, d){
42109             
42110             this.setValue(d);
42111             this.fireEvent('select', this, d);
42112         },
42113         show : function(){ // retain focus styling
42114             this.onFocus();
42115         },
42116         hide : function(){
42117             this.focus.defer(10, this);
42118             var ml = this.menuListeners;
42119             this.menu.un("select", ml.select,  this);
42120             this.menu.un("show", ml.show,  this);
42121             this.menu.un("hide", ml.hide,  this);
42122         }
42123     },
42124
42125     // private
42126     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42127     onTriggerClick : function(){
42128         if(this.disabled){
42129             return;
42130         }
42131         if(this.menu == null){
42132             this.menu = new Roo.menu.DateMenu();
42133         }
42134         Roo.apply(this.menu.picker,  {
42135             showClear: this.allowBlank,
42136             minDate : this.minValue,
42137             maxDate : this.maxValue,
42138             disabledDatesRE : this.ddMatch,
42139             disabledDatesText : this.disabledDatesText,
42140             disabledDays : this.disabledDays,
42141             disabledDaysText : this.disabledDaysText,
42142             format : this.useIso ? 'Y-m-d' : this.format,
42143             minText : String.format(this.minText, this.formatDate(this.minValue)),
42144             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42145         });
42146         this.menu.on(Roo.apply({}, this.menuListeners, {
42147             scope:this
42148         }));
42149         this.menu.picker.setValue(this.getValue() || new Date());
42150         this.menu.show(this.el, "tl-bl?");
42151     },
42152
42153     beforeBlur : function(){
42154         var v = this.parseDate(this.getRawValue());
42155         if(v){
42156             this.setValue(v);
42157         }
42158     },
42159
42160     /*@
42161      * overide
42162      * 
42163      */
42164     isDirty : function() {
42165         if(this.disabled) {
42166             return false;
42167         }
42168         
42169         if(typeof(this.startValue) === 'undefined'){
42170             return false;
42171         }
42172         
42173         return String(this.getValue()) !== String(this.startValue);
42174         
42175     },
42176     // @overide
42177     cleanLeadingSpace : function(e)
42178     {
42179        return;
42180     }
42181     
42182 });/*
42183  * Based on:
42184  * Ext JS Library 1.1.1
42185  * Copyright(c) 2006-2007, Ext JS, LLC.
42186  *
42187  * Originally Released Under LGPL - original licence link has changed is not relivant.
42188  *
42189  * Fork - LGPL
42190  * <script type="text/javascript">
42191  */
42192  
42193 /**
42194  * @class Roo.form.MonthField
42195  * @extends Roo.form.TriggerField
42196  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
42197 * @constructor
42198 * Create a new MonthField
42199 * @param {Object} config
42200  */
42201 Roo.form.MonthField = function(config){
42202     
42203     Roo.form.MonthField.superclass.constructor.call(this, config);
42204     
42205       this.addEvents({
42206          
42207         /**
42208          * @event select
42209          * Fires when a date is selected
42210              * @param {Roo.form.MonthFieeld} combo This combo box
42211              * @param {Date} date The date selected
42212              */
42213         'select' : true
42214          
42215     });
42216     
42217     
42218     if(typeof this.minValue == "string") {
42219         this.minValue = this.parseDate(this.minValue);
42220     }
42221     if(typeof this.maxValue == "string") {
42222         this.maxValue = this.parseDate(this.maxValue);
42223     }
42224     this.ddMatch = null;
42225     if(this.disabledDates){
42226         var dd = this.disabledDates;
42227         var re = "(?:";
42228         for(var i = 0; i < dd.length; i++){
42229             re += dd[i];
42230             if(i != dd.length-1) {
42231                 re += "|";
42232             }
42233         }
42234         this.ddMatch = new RegExp(re + ")");
42235     }
42236 };
42237
42238 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
42239     /**
42240      * @cfg {String} format
42241      * The default date format string which can be overriden for localization support.  The format must be
42242      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
42243      */
42244     format : "M Y",
42245     /**
42246      * @cfg {String} altFormats
42247      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
42248      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
42249      */
42250     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
42251     /**
42252      * @cfg {Array} disabledDays
42253      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
42254      */
42255     disabledDays : [0,1,2,3,4,5,6],
42256     /**
42257      * @cfg {String} disabledDaysText
42258      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
42259      */
42260     disabledDaysText : "Disabled",
42261     /**
42262      * @cfg {Array} disabledDates
42263      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
42264      * expression so they are very powerful. Some examples:
42265      * <ul>
42266      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
42267      * <li>["03/08", "09/16"] would disable those days for every year</li>
42268      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
42269      * <li>["03/../2006"] would disable every day in March 2006</li>
42270      * <li>["^03"] would disable every day in every March</li>
42271      * </ul>
42272      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
42273      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
42274      */
42275     disabledDates : null,
42276     /**
42277      * @cfg {String} disabledDatesText
42278      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
42279      */
42280     disabledDatesText : "Disabled",
42281     /**
42282      * @cfg {Date/String} minValue
42283      * The minimum allowed date. Can be either a Javascript date object or a string date in a
42284      * valid format (defaults to null).
42285      */
42286     minValue : null,
42287     /**
42288      * @cfg {Date/String} maxValue
42289      * The maximum allowed date. Can be either a Javascript date object or a string date in a
42290      * valid format (defaults to null).
42291      */
42292     maxValue : null,
42293     /**
42294      * @cfg {String} minText
42295      * The error text to display when the date in the cell is before minValue (defaults to
42296      * 'The date in this field must be after {minValue}').
42297      */
42298     minText : "The date in this field must be equal to or after {0}",
42299     /**
42300      * @cfg {String} maxTextf
42301      * The error text to display when the date in the cell is after maxValue (defaults to
42302      * 'The date in this field must be before {maxValue}').
42303      */
42304     maxText : "The date in this field must be equal to or before {0}",
42305     /**
42306      * @cfg {String} invalidText
42307      * The error text to display when the date in the field is invalid (defaults to
42308      * '{value} is not a valid date - it must be in the format {format}').
42309      */
42310     invalidText : "{0} is not a valid date - it must be in the format {1}",
42311     /**
42312      * @cfg {String} triggerClass
42313      * An additional CSS class used to style the trigger button.  The trigger will always get the
42314      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
42315      * which displays a calendar icon).
42316      */
42317     triggerClass : 'x-form-date-trigger',
42318     
42319
42320     /**
42321      * @cfg {Boolean} useIso
42322      * if enabled, then the date field will use a hidden field to store the 
42323      * real value as iso formated date. default (true)
42324      */ 
42325     useIso : true,
42326     /**
42327      * @cfg {String/Object} autoCreate
42328      * A DomHelper element spec, or true for a default element spec (defaults to
42329      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
42330      */ 
42331     // private
42332     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
42333     
42334     // private
42335     hiddenField: false,
42336     
42337     hideMonthPicker : false,
42338     
42339     onRender : function(ct, position)
42340     {
42341         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
42342         if (this.useIso) {
42343             this.el.dom.removeAttribute('name'); 
42344             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
42345                     'before', true);
42346             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
42347             // prevent input submission
42348             this.hiddenName = this.name;
42349         }
42350             
42351             
42352     },
42353     
42354     // private
42355     validateValue : function(value)
42356     {
42357         value = this.formatDate(value);
42358         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
42359             return false;
42360         }
42361         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
42362              return true;
42363         }
42364         var svalue = value;
42365         value = this.parseDate(value);
42366         if(!value){
42367             this.markInvalid(String.format(this.invalidText, svalue, this.format));
42368             return false;
42369         }
42370         var time = value.getTime();
42371         if(this.minValue && time < this.minValue.getTime()){
42372             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42373             return false;
42374         }
42375         if(this.maxValue && time > this.maxValue.getTime()){
42376             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42377             return false;
42378         }
42379         /*if(this.disabledDays){
42380             var day = value.getDay();
42381             for(var i = 0; i < this.disabledDays.length; i++) {
42382                 if(day === this.disabledDays[i]){
42383                     this.markInvalid(this.disabledDaysText);
42384                     return false;
42385                 }
42386             }
42387         }
42388         */
42389         var fvalue = this.formatDate(value);
42390         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42391             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42392             return false;
42393         }
42394         */
42395         return true;
42396     },
42397
42398     // private
42399     // Provides logic to override the default TriggerField.validateBlur which just returns true
42400     validateBlur : function(){
42401         return !this.menu || !this.menu.isVisible();
42402     },
42403
42404     /**
42405      * Returns the current date value of the date field.
42406      * @return {Date} The date value
42407      */
42408     getValue : function(){
42409         
42410         
42411         
42412         return  this.hiddenField ?
42413                 this.hiddenField.value :
42414                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42415     },
42416
42417     /**
42418      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42419      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42420      * (the default format used is "m/d/y").
42421      * <br />Usage:
42422      * <pre><code>
42423 //All of these calls set the same date value (May 4, 2006)
42424
42425 //Pass a date object:
42426 var dt = new Date('5/4/06');
42427 monthField.setValue(dt);
42428
42429 //Pass a date string (default format):
42430 monthField.setValue('5/4/06');
42431
42432 //Pass a date string (custom format):
42433 monthField.format = 'Y-m-d';
42434 monthField.setValue('2006-5-4');
42435 </code></pre>
42436      * @param {String/Date} date The date or valid date string
42437      */
42438     setValue : function(date){
42439         Roo.log('month setValue' + date);
42440         // can only be first of month..
42441         
42442         var val = this.parseDate(date);
42443         
42444         if (this.hiddenField) {
42445             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42446         }
42447         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42448         this.value = this.parseDate(date);
42449     },
42450
42451     // private
42452     parseDate : function(value){
42453         if(!value || value instanceof Date){
42454             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42455             return value;
42456         }
42457         var v = Date.parseDate(value, this.format);
42458         if (!v && this.useIso) {
42459             v = Date.parseDate(value, 'Y-m-d');
42460         }
42461         if (v) {
42462             // 
42463             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42464         }
42465         
42466         
42467         if(!v && this.altFormats){
42468             if(!this.altFormatsArray){
42469                 this.altFormatsArray = this.altFormats.split("|");
42470             }
42471             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42472                 v = Date.parseDate(value, this.altFormatsArray[i]);
42473             }
42474         }
42475         return v;
42476     },
42477
42478     // private
42479     formatDate : function(date, fmt){
42480         return (!date || !(date instanceof Date)) ?
42481                date : date.dateFormat(fmt || this.format);
42482     },
42483
42484     // private
42485     menuListeners : {
42486         select: function(m, d){
42487             this.setValue(d);
42488             this.fireEvent('select', this, d);
42489         },
42490         show : function(){ // retain focus styling
42491             this.onFocus();
42492         },
42493         hide : function(){
42494             this.focus.defer(10, this);
42495             var ml = this.menuListeners;
42496             this.menu.un("select", ml.select,  this);
42497             this.menu.un("show", ml.show,  this);
42498             this.menu.un("hide", ml.hide,  this);
42499         }
42500     },
42501     // private
42502     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42503     onTriggerClick : function(){
42504         if(this.disabled){
42505             return;
42506         }
42507         if(this.menu == null){
42508             this.menu = new Roo.menu.DateMenu();
42509            
42510         }
42511         
42512         Roo.apply(this.menu.picker,  {
42513             
42514             showClear: this.allowBlank,
42515             minDate : this.minValue,
42516             maxDate : this.maxValue,
42517             disabledDatesRE : this.ddMatch,
42518             disabledDatesText : this.disabledDatesText,
42519             
42520             format : this.useIso ? 'Y-m-d' : this.format,
42521             minText : String.format(this.minText, this.formatDate(this.minValue)),
42522             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42523             
42524         });
42525          this.menu.on(Roo.apply({}, this.menuListeners, {
42526             scope:this
42527         }));
42528        
42529         
42530         var m = this.menu;
42531         var p = m.picker;
42532         
42533         // hide month picker get's called when we called by 'before hide';
42534         
42535         var ignorehide = true;
42536         p.hideMonthPicker  = function(disableAnim){
42537             if (ignorehide) {
42538                 return;
42539             }
42540              if(this.monthPicker){
42541                 Roo.log("hideMonthPicker called");
42542                 if(disableAnim === true){
42543                     this.monthPicker.hide();
42544                 }else{
42545                     this.monthPicker.slideOut('t', {duration:.2});
42546                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42547                     p.fireEvent("select", this, this.value);
42548                     m.hide();
42549                 }
42550             }
42551         }
42552         
42553         Roo.log('picker set value');
42554         Roo.log(this.getValue());
42555         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42556         m.show(this.el, 'tl-bl?');
42557         ignorehide  = false;
42558         // this will trigger hideMonthPicker..
42559         
42560         
42561         // hidden the day picker
42562         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42563         
42564         
42565         
42566       
42567         
42568         p.showMonthPicker.defer(100, p);
42569     
42570         
42571        
42572     },
42573
42574     beforeBlur : function(){
42575         var v = this.parseDate(this.getRawValue());
42576         if(v){
42577             this.setValue(v);
42578         }
42579     }
42580
42581     /** @cfg {Boolean} grow @hide */
42582     /** @cfg {Number} growMin @hide */
42583     /** @cfg {Number} growMax @hide */
42584     /**
42585      * @hide
42586      * @method autoSize
42587      */
42588 });/*
42589  * Based on:
42590  * Ext JS Library 1.1.1
42591  * Copyright(c) 2006-2007, Ext JS, LLC.
42592  *
42593  * Originally Released Under LGPL - original licence link has changed is not relivant.
42594  *
42595  * Fork - LGPL
42596  * <script type="text/javascript">
42597  */
42598  
42599
42600 /**
42601  * @class Roo.form.ComboBox
42602  * @extends Roo.form.TriggerField
42603  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42604  * @constructor
42605  * Create a new ComboBox.
42606  * @param {Object} config Configuration options
42607  */
42608 Roo.form.ComboBox = function(config){
42609     Roo.form.ComboBox.superclass.constructor.call(this, config);
42610     this.addEvents({
42611         /**
42612          * @event expand
42613          * Fires when the dropdown list is expanded
42614              * @param {Roo.form.ComboBox} combo This combo box
42615              */
42616         'expand' : true,
42617         /**
42618          * @event collapse
42619          * Fires when the dropdown list is collapsed
42620              * @param {Roo.form.ComboBox} combo This combo box
42621              */
42622         'collapse' : true,
42623         /**
42624          * @event beforeselect
42625          * Fires before a list item is selected. Return false to cancel the selection.
42626              * @param {Roo.form.ComboBox} combo This combo box
42627              * @param {Roo.data.Record} record The data record returned from the underlying store
42628              * @param {Number} index The index of the selected item in the dropdown list
42629              */
42630         'beforeselect' : true,
42631         /**
42632          * @event select
42633          * Fires when a list item is selected
42634              * @param {Roo.form.ComboBox} combo This combo box
42635              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42636              * @param {Number} index The index of the selected item in the dropdown list
42637              */
42638         'select' : true,
42639         /**
42640          * @event beforequery
42641          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42642          * The event object passed has these properties:
42643              * @param {Roo.form.ComboBox} combo This combo box
42644              * @param {String} query The query
42645              * @param {Boolean} forceAll true to force "all" query
42646              * @param {Boolean} cancel true to cancel the query
42647              * @param {Object} e The query event object
42648              */
42649         'beforequery': true,
42650          /**
42651          * @event add
42652          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42653              * @param {Roo.form.ComboBox} combo This combo box
42654              */
42655         'add' : true,
42656         /**
42657          * @event edit
42658          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42659              * @param {Roo.form.ComboBox} combo This combo box
42660              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42661              */
42662         'edit' : true
42663         
42664         
42665     });
42666     if(this.transform){
42667         this.allowDomMove = false;
42668         var s = Roo.getDom(this.transform);
42669         if(!this.hiddenName){
42670             this.hiddenName = s.name;
42671         }
42672         if(!this.store){
42673             this.mode = 'local';
42674             var d = [], opts = s.options;
42675             for(var i = 0, len = opts.length;i < len; i++){
42676                 var o = opts[i];
42677                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42678                 if(o.selected) {
42679                     this.value = value;
42680                 }
42681                 d.push([value, o.text]);
42682             }
42683             this.store = new Roo.data.SimpleStore({
42684                 'id': 0,
42685                 fields: ['value', 'text'],
42686                 data : d
42687             });
42688             this.valueField = 'value';
42689             this.displayField = 'text';
42690         }
42691         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42692         if(!this.lazyRender){
42693             this.target = true;
42694             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42695             s.parentNode.removeChild(s); // remove it
42696             this.render(this.el.parentNode);
42697         }else{
42698             s.parentNode.removeChild(s); // remove it
42699         }
42700
42701     }
42702     if (this.store) {
42703         this.store = Roo.factory(this.store, Roo.data);
42704     }
42705     
42706     this.selectedIndex = -1;
42707     if(this.mode == 'local'){
42708         if(config.queryDelay === undefined){
42709             this.queryDelay = 10;
42710         }
42711         if(config.minChars === undefined){
42712             this.minChars = 0;
42713         }
42714     }
42715 };
42716
42717 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42718     /**
42719      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42720      */
42721     /**
42722      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42723      * rendering into an Roo.Editor, defaults to false)
42724      */
42725     /**
42726      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42727      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42728      */
42729     /**
42730      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42731      */
42732     /**
42733      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42734      * the dropdown list (defaults to undefined, with no header element)
42735      */
42736
42737      /**
42738      * @cfg {String/Roo.Template} tpl The template to use to render the output
42739      */
42740      
42741     // private
42742     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42743     /**
42744      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42745      */
42746     listWidth: undefined,
42747     /**
42748      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42749      * mode = 'remote' or 'text' if mode = 'local')
42750      */
42751     displayField: undefined,
42752     /**
42753      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42754      * mode = 'remote' or 'value' if mode = 'local'). 
42755      * Note: use of a valueField requires the user make a selection
42756      * in order for a value to be mapped.
42757      */
42758     valueField: undefined,
42759     
42760     
42761     /**
42762      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42763      * field's data value (defaults to the underlying DOM element's name)
42764      */
42765     hiddenName: undefined,
42766     /**
42767      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42768      */
42769     listClass: '',
42770     /**
42771      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42772      */
42773     selectedClass: 'x-combo-selected',
42774     /**
42775      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42776      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42777      * which displays a downward arrow icon).
42778      */
42779     triggerClass : 'x-form-arrow-trigger',
42780     /**
42781      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42782      */
42783     shadow:'sides',
42784     /**
42785      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42786      * anchor positions (defaults to 'tl-bl')
42787      */
42788     listAlign: 'tl-bl?',
42789     /**
42790      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42791      */
42792     maxHeight: 300,
42793     /**
42794      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42795      * query specified by the allQuery config option (defaults to 'query')
42796      */
42797     triggerAction: 'query',
42798     /**
42799      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42800      * (defaults to 4, does not apply if editable = false)
42801      */
42802     minChars : 4,
42803     /**
42804      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42805      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42806      */
42807     typeAhead: false,
42808     /**
42809      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42810      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42811      */
42812     queryDelay: 500,
42813     /**
42814      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42815      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42816      */
42817     pageSize: 0,
42818     /**
42819      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42820      * when editable = true (defaults to false)
42821      */
42822     selectOnFocus:false,
42823     /**
42824      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42825      */
42826     queryParam: 'query',
42827     /**
42828      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42829      * when mode = 'remote' (defaults to 'Loading...')
42830      */
42831     loadingText: 'Loading...',
42832     /**
42833      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42834      */
42835     resizable: false,
42836     /**
42837      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42838      */
42839     handleHeight : 8,
42840     /**
42841      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42842      * traditional select (defaults to true)
42843      */
42844     editable: true,
42845     /**
42846      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42847      */
42848     allQuery: '',
42849     /**
42850      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42851      */
42852     mode: 'remote',
42853     /**
42854      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42855      * listWidth has a higher value)
42856      */
42857     minListWidth : 70,
42858     /**
42859      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42860      * allow the user to set arbitrary text into the field (defaults to false)
42861      */
42862     forceSelection:false,
42863     /**
42864      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42865      * if typeAhead = true (defaults to 250)
42866      */
42867     typeAheadDelay : 250,
42868     /**
42869      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42870      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42871      */
42872     valueNotFoundText : undefined,
42873     /**
42874      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42875      */
42876     blockFocus : false,
42877     
42878     /**
42879      * @cfg {Boolean} disableClear Disable showing of clear button.
42880      */
42881     disableClear : false,
42882     /**
42883      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42884      */
42885     alwaysQuery : false,
42886     
42887     //private
42888     addicon : false,
42889     editicon: false,
42890     
42891     // element that contains real text value.. (when hidden is used..)
42892      
42893     // private
42894     onRender : function(ct, position)
42895     {
42896         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42897         
42898         if(this.hiddenName){
42899             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42900                     'before', true);
42901             this.hiddenField.value =
42902                 this.hiddenValue !== undefined ? this.hiddenValue :
42903                 this.value !== undefined ? this.value : '';
42904
42905             // prevent input submission
42906             this.el.dom.removeAttribute('name');
42907              
42908              
42909         }
42910         
42911         if(Roo.isGecko){
42912             this.el.dom.setAttribute('autocomplete', 'off');
42913         }
42914
42915         var cls = 'x-combo-list';
42916
42917         this.list = new Roo.Layer({
42918             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42919         });
42920
42921         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42922         this.list.setWidth(lw);
42923         this.list.swallowEvent('mousewheel');
42924         this.assetHeight = 0;
42925
42926         if(this.title){
42927             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42928             this.assetHeight += this.header.getHeight();
42929         }
42930
42931         this.innerList = this.list.createChild({cls:cls+'-inner'});
42932         this.innerList.on('mouseover', this.onViewOver, this);
42933         this.innerList.on('mousemove', this.onViewMove, this);
42934         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42935         
42936         if(this.allowBlank && !this.pageSize && !this.disableClear){
42937             this.footer = this.list.createChild({cls:cls+'-ft'});
42938             this.pageTb = new Roo.Toolbar(this.footer);
42939            
42940         }
42941         if(this.pageSize){
42942             this.footer = this.list.createChild({cls:cls+'-ft'});
42943             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42944                     {pageSize: this.pageSize});
42945             
42946         }
42947         
42948         if (this.pageTb && this.allowBlank && !this.disableClear) {
42949             var _this = this;
42950             this.pageTb.add(new Roo.Toolbar.Fill(), {
42951                 cls: 'x-btn-icon x-btn-clear',
42952                 text: '&#160;',
42953                 handler: function()
42954                 {
42955                     _this.collapse();
42956                     _this.clearValue();
42957                     _this.onSelect(false, -1);
42958                 }
42959             });
42960         }
42961         if (this.footer) {
42962             this.assetHeight += this.footer.getHeight();
42963         }
42964         
42965
42966         if(!this.tpl){
42967             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42968         }
42969
42970         this.view = new Roo.View(this.innerList, this.tpl, {
42971             singleSelect:true,
42972             store: this.store,
42973             selectedClass: this.selectedClass
42974         });
42975
42976         this.view.on('click', this.onViewClick, this);
42977
42978         this.store.on('beforeload', this.onBeforeLoad, this);
42979         this.store.on('load', this.onLoad, this);
42980         this.store.on('loadexception', this.onLoadException, this);
42981
42982         if(this.resizable){
42983             this.resizer = new Roo.Resizable(this.list,  {
42984                pinned:true, handles:'se'
42985             });
42986             this.resizer.on('resize', function(r, w, h){
42987                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42988                 this.listWidth = w;
42989                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42990                 this.restrictHeight();
42991             }, this);
42992             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42993         }
42994         if(!this.editable){
42995             this.editable = true;
42996             this.setEditable(false);
42997         }  
42998         
42999         
43000         if (typeof(this.events.add.listeners) != 'undefined') {
43001             
43002             this.addicon = this.wrap.createChild(
43003                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
43004        
43005             this.addicon.on('click', function(e) {
43006                 this.fireEvent('add', this);
43007             }, this);
43008         }
43009         if (typeof(this.events.edit.listeners) != 'undefined') {
43010             
43011             this.editicon = this.wrap.createChild(
43012                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
43013             if (this.addicon) {
43014                 this.editicon.setStyle('margin-left', '40px');
43015             }
43016             this.editicon.on('click', function(e) {
43017                 
43018                 // we fire even  if inothing is selected..
43019                 this.fireEvent('edit', this, this.lastData );
43020                 
43021             }, this);
43022         }
43023         
43024         
43025         
43026     },
43027
43028     // private
43029     initEvents : function(){
43030         Roo.form.ComboBox.superclass.initEvents.call(this);
43031
43032         this.keyNav = new Roo.KeyNav(this.el, {
43033             "up" : function(e){
43034                 this.inKeyMode = true;
43035                 this.selectPrev();
43036             },
43037
43038             "down" : function(e){
43039                 if(!this.isExpanded()){
43040                     this.onTriggerClick();
43041                 }else{
43042                     this.inKeyMode = true;
43043                     this.selectNext();
43044                 }
43045             },
43046
43047             "enter" : function(e){
43048                 this.onViewClick();
43049                 //return true;
43050             },
43051
43052             "esc" : function(e){
43053                 this.collapse();
43054             },
43055
43056             "tab" : function(e){
43057                 this.onViewClick(false);
43058                 this.fireEvent("specialkey", this, e);
43059                 return true;
43060             },
43061
43062             scope : this,
43063
43064             doRelay : function(foo, bar, hname){
43065                 if(hname == 'down' || this.scope.isExpanded()){
43066                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43067                 }
43068                 return true;
43069             },
43070
43071             forceKeyDown: true
43072         });
43073         this.queryDelay = Math.max(this.queryDelay || 10,
43074                 this.mode == 'local' ? 10 : 250);
43075         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
43076         if(this.typeAhead){
43077             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
43078         }
43079         if(this.editable !== false){
43080             this.el.on("keyup", this.onKeyUp, this);
43081         }
43082         if(this.forceSelection){
43083             this.on('blur', this.doForce, this);
43084         }
43085     },
43086
43087     onDestroy : function(){
43088         if(this.view){
43089             this.view.setStore(null);
43090             this.view.el.removeAllListeners();
43091             this.view.el.remove();
43092             this.view.purgeListeners();
43093         }
43094         if(this.list){
43095             this.list.destroy();
43096         }
43097         if(this.store){
43098             this.store.un('beforeload', this.onBeforeLoad, this);
43099             this.store.un('load', this.onLoad, this);
43100             this.store.un('loadexception', this.onLoadException, this);
43101         }
43102         Roo.form.ComboBox.superclass.onDestroy.call(this);
43103     },
43104
43105     // private
43106     fireKey : function(e){
43107         if(e.isNavKeyPress() && !this.list.isVisible()){
43108             this.fireEvent("specialkey", this, e);
43109         }
43110     },
43111
43112     // private
43113     onResize: function(w, h){
43114         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
43115         
43116         if(typeof w != 'number'){
43117             // we do not handle it!?!?
43118             return;
43119         }
43120         var tw = this.trigger.getWidth();
43121         tw += this.addicon ? this.addicon.getWidth() : 0;
43122         tw += this.editicon ? this.editicon.getWidth() : 0;
43123         var x = w - tw;
43124         this.el.setWidth( this.adjustWidth('input', x));
43125             
43126         this.trigger.setStyle('left', x+'px');
43127         
43128         if(this.list && this.listWidth === undefined){
43129             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
43130             this.list.setWidth(lw);
43131             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43132         }
43133         
43134     
43135         
43136     },
43137
43138     /**
43139      * Allow or prevent the user from directly editing the field text.  If false is passed,
43140      * the user will only be able to select from the items defined in the dropdown list.  This method
43141      * is the runtime equivalent of setting the 'editable' config option at config time.
43142      * @param {Boolean} value True to allow the user to directly edit the field text
43143      */
43144     setEditable : function(value){
43145         if(value == this.editable){
43146             return;
43147         }
43148         this.editable = value;
43149         if(!value){
43150             this.el.dom.setAttribute('readOnly', true);
43151             this.el.on('mousedown', this.onTriggerClick,  this);
43152             this.el.addClass('x-combo-noedit');
43153         }else{
43154             this.el.dom.setAttribute('readOnly', false);
43155             this.el.un('mousedown', this.onTriggerClick,  this);
43156             this.el.removeClass('x-combo-noedit');
43157         }
43158     },
43159
43160     // private
43161     onBeforeLoad : function(){
43162         if(!this.hasFocus){
43163             return;
43164         }
43165         this.innerList.update(this.loadingText ?
43166                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43167         this.restrictHeight();
43168         this.selectedIndex = -1;
43169     },
43170
43171     // private
43172     onLoad : function(){
43173         if(!this.hasFocus){
43174             return;
43175         }
43176         if(this.store.getCount() > 0){
43177             this.expand();
43178             this.restrictHeight();
43179             if(this.lastQuery == this.allQuery){
43180                 if(this.editable){
43181                     this.el.dom.select();
43182                 }
43183                 if(!this.selectByValue(this.value, true)){
43184                     this.select(0, true);
43185                 }
43186             }else{
43187                 this.selectNext();
43188                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
43189                     this.taTask.delay(this.typeAheadDelay);
43190                 }
43191             }
43192         }else{
43193             this.onEmptyResults();
43194         }
43195         //this.el.focus();
43196     },
43197     // private
43198     onLoadException : function()
43199     {
43200         this.collapse();
43201         Roo.log(this.store.reader.jsonData);
43202         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43203             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43204         }
43205         
43206         
43207     },
43208     // private
43209     onTypeAhead : function(){
43210         if(this.store.getCount() > 0){
43211             var r = this.store.getAt(0);
43212             var newValue = r.data[this.displayField];
43213             var len = newValue.length;
43214             var selStart = this.getRawValue().length;
43215             if(selStart != len){
43216                 this.setRawValue(newValue);
43217                 this.selectText(selStart, newValue.length);
43218             }
43219         }
43220     },
43221
43222     // private
43223     onSelect : function(record, index){
43224         if(this.fireEvent('beforeselect', this, record, index) !== false){
43225             this.setFromData(index > -1 ? record.data : false);
43226             this.collapse();
43227             this.fireEvent('select', this, record, index);
43228         }
43229     },
43230
43231     /**
43232      * Returns the currently selected field value or empty string if no value is set.
43233      * @return {String} value The selected value
43234      */
43235     getValue : function(){
43236         if(this.valueField){
43237             return typeof this.value != 'undefined' ? this.value : '';
43238         }
43239         return Roo.form.ComboBox.superclass.getValue.call(this);
43240     },
43241
43242     /**
43243      * Clears any text/value currently set in the field
43244      */
43245     clearValue : function(){
43246         if(this.hiddenField){
43247             this.hiddenField.value = '';
43248         }
43249         this.value = '';
43250         this.setRawValue('');
43251         this.lastSelectionText = '';
43252         
43253     },
43254
43255     /**
43256      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
43257      * will be displayed in the field.  If the value does not match the data value of an existing item,
43258      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
43259      * Otherwise the field will be blank (although the value will still be set).
43260      * @param {String} value The value to match
43261      */
43262     setValue : function(v){
43263         var text = v;
43264         if(this.valueField){
43265             var r = this.findRecord(this.valueField, v);
43266             if(r){
43267                 text = r.data[this.displayField];
43268             }else if(this.valueNotFoundText !== undefined){
43269                 text = this.valueNotFoundText;
43270             }
43271         }
43272         this.lastSelectionText = text;
43273         if(this.hiddenField){
43274             this.hiddenField.value = v;
43275         }
43276         Roo.form.ComboBox.superclass.setValue.call(this, text);
43277         this.value = v;
43278     },
43279     /**
43280      * @property {Object} the last set data for the element
43281      */
43282     
43283     lastData : false,
43284     /**
43285      * Sets the value of the field based on a object which is related to the record format for the store.
43286      * @param {Object} value the value to set as. or false on reset?
43287      */
43288     setFromData : function(o){
43289         var dv = ''; // display value
43290         var vv = ''; // value value..
43291         this.lastData = o;
43292         if (this.displayField) {
43293             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
43294         } else {
43295             // this is an error condition!!!
43296             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
43297         }
43298         
43299         if(this.valueField){
43300             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
43301         }
43302         if(this.hiddenField){
43303             this.hiddenField.value = vv;
43304             
43305             this.lastSelectionText = dv;
43306             Roo.form.ComboBox.superclass.setValue.call(this, dv);
43307             this.value = vv;
43308             return;
43309         }
43310         // no hidden field.. - we store the value in 'value', but still display
43311         // display field!!!!
43312         this.lastSelectionText = dv;
43313         Roo.form.ComboBox.superclass.setValue.call(this, dv);
43314         this.value = vv;
43315         
43316         
43317     },
43318     // private
43319     reset : function(){
43320         // overridden so that last data is reset..
43321         this.setValue(this.resetValue);
43322         this.originalValue = this.getValue();
43323         this.clearInvalid();
43324         this.lastData = false;
43325         if (this.view) {
43326             this.view.clearSelections();
43327         }
43328     },
43329     // private
43330     findRecord : function(prop, value){
43331         var record;
43332         if(this.store.getCount() > 0){
43333             this.store.each(function(r){
43334                 if(r.data[prop] == value){
43335                     record = r;
43336                     return false;
43337                 }
43338                 return true;
43339             });
43340         }
43341         return record;
43342     },
43343     
43344     getName: function()
43345     {
43346         // returns hidden if it's set..
43347         if (!this.rendered) {return ''};
43348         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
43349         
43350     },
43351     // private
43352     onViewMove : function(e, t){
43353         this.inKeyMode = false;
43354     },
43355
43356     // private
43357     onViewOver : function(e, t){
43358         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
43359             return;
43360         }
43361         var item = this.view.findItemFromChild(t);
43362         if(item){
43363             var index = this.view.indexOf(item);
43364             this.select(index, false);
43365         }
43366     },
43367
43368     // private
43369     onViewClick : function(doFocus)
43370     {
43371         var index = this.view.getSelectedIndexes()[0];
43372         var r = this.store.getAt(index);
43373         if(r){
43374             this.onSelect(r, index);
43375         }
43376         if(doFocus !== false && !this.blockFocus){
43377             this.el.focus();
43378         }
43379     },
43380
43381     // private
43382     restrictHeight : function(){
43383         this.innerList.dom.style.height = '';
43384         var inner = this.innerList.dom;
43385         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43386         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43387         this.list.beginUpdate();
43388         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43389         this.list.alignTo(this.el, this.listAlign);
43390         this.list.endUpdate();
43391     },
43392
43393     // private
43394     onEmptyResults : function(){
43395         this.collapse();
43396     },
43397
43398     /**
43399      * Returns true if the dropdown list is expanded, else false.
43400      */
43401     isExpanded : function(){
43402         return this.list.isVisible();
43403     },
43404
43405     /**
43406      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43407      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43408      * @param {String} value The data value of the item to select
43409      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43410      * selected item if it is not currently in view (defaults to true)
43411      * @return {Boolean} True if the value matched an item in the list, else false
43412      */
43413     selectByValue : function(v, scrollIntoView){
43414         if(v !== undefined && v !== null){
43415             var r = this.findRecord(this.valueField || this.displayField, v);
43416             if(r){
43417                 this.select(this.store.indexOf(r), scrollIntoView);
43418                 return true;
43419             }
43420         }
43421         return false;
43422     },
43423
43424     /**
43425      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43426      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43427      * @param {Number} index The zero-based index of the list item to select
43428      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43429      * selected item if it is not currently in view (defaults to true)
43430      */
43431     select : function(index, scrollIntoView){
43432         this.selectedIndex = index;
43433         this.view.select(index);
43434         if(scrollIntoView !== false){
43435             var el = this.view.getNode(index);
43436             if(el){
43437                 this.innerList.scrollChildIntoView(el, false);
43438             }
43439         }
43440     },
43441
43442     // private
43443     selectNext : function(){
43444         var ct = this.store.getCount();
43445         if(ct > 0){
43446             if(this.selectedIndex == -1){
43447                 this.select(0);
43448             }else if(this.selectedIndex < ct-1){
43449                 this.select(this.selectedIndex+1);
43450             }
43451         }
43452     },
43453
43454     // private
43455     selectPrev : function(){
43456         var ct = this.store.getCount();
43457         if(ct > 0){
43458             if(this.selectedIndex == -1){
43459                 this.select(0);
43460             }else if(this.selectedIndex != 0){
43461                 this.select(this.selectedIndex-1);
43462             }
43463         }
43464     },
43465
43466     // private
43467     onKeyUp : function(e){
43468         if(this.editable !== false && !e.isSpecialKey()){
43469             this.lastKey = e.getKey();
43470             this.dqTask.delay(this.queryDelay);
43471         }
43472     },
43473
43474     // private
43475     validateBlur : function(){
43476         return !this.list || !this.list.isVisible();   
43477     },
43478
43479     // private
43480     initQuery : function(){
43481         this.doQuery(this.getRawValue());
43482     },
43483
43484     // private
43485     doForce : function(){
43486         if(this.el.dom.value.length > 0){
43487             this.el.dom.value =
43488                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43489              
43490         }
43491     },
43492
43493     /**
43494      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43495      * query allowing the query action to be canceled if needed.
43496      * @param {String} query The SQL query to execute
43497      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43498      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43499      * saved in the current store (defaults to false)
43500      */
43501     doQuery : function(q, forceAll){
43502         if(q === undefined || q === null){
43503             q = '';
43504         }
43505         var qe = {
43506             query: q,
43507             forceAll: forceAll,
43508             combo: this,
43509             cancel:false
43510         };
43511         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43512             return false;
43513         }
43514         q = qe.query;
43515         forceAll = qe.forceAll;
43516         if(forceAll === true || (q.length >= this.minChars)){
43517             if(this.lastQuery != q || this.alwaysQuery){
43518                 this.lastQuery = q;
43519                 if(this.mode == 'local'){
43520                     this.selectedIndex = -1;
43521                     if(forceAll){
43522                         this.store.clearFilter();
43523                     }else{
43524                         this.store.filter(this.displayField, q);
43525                     }
43526                     this.onLoad();
43527                 }else{
43528                     this.store.baseParams[this.queryParam] = q;
43529                     this.store.load({
43530                         params: this.getParams(q)
43531                     });
43532                     this.expand();
43533                 }
43534             }else{
43535                 this.selectedIndex = -1;
43536                 this.onLoad();   
43537             }
43538         }
43539     },
43540
43541     // private
43542     getParams : function(q){
43543         var p = {};
43544         //p[this.queryParam] = q;
43545         if(this.pageSize){
43546             p.start = 0;
43547             p.limit = this.pageSize;
43548         }
43549         return p;
43550     },
43551
43552     /**
43553      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43554      */
43555     collapse : function(){
43556         if(!this.isExpanded()){
43557             return;
43558         }
43559         this.list.hide();
43560         Roo.get(document).un('mousedown', this.collapseIf, this);
43561         Roo.get(document).un('mousewheel', this.collapseIf, this);
43562         if (!this.editable) {
43563             Roo.get(document).un('keydown', this.listKeyPress, this);
43564         }
43565         this.fireEvent('collapse', this);
43566     },
43567
43568     // private
43569     collapseIf : function(e){
43570         if(!e.within(this.wrap) && !e.within(this.list)){
43571             this.collapse();
43572         }
43573     },
43574
43575     /**
43576      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43577      */
43578     expand : function(){
43579         if(this.isExpanded() || !this.hasFocus){
43580             return;
43581         }
43582         this.list.alignTo(this.el, this.listAlign);
43583         this.list.show();
43584         Roo.get(document).on('mousedown', this.collapseIf, this);
43585         Roo.get(document).on('mousewheel', this.collapseIf, this);
43586         if (!this.editable) {
43587             Roo.get(document).on('keydown', this.listKeyPress, this);
43588         }
43589         
43590         this.fireEvent('expand', this);
43591     },
43592
43593     // private
43594     // Implements the default empty TriggerField.onTriggerClick function
43595     onTriggerClick : function(){
43596         if(this.disabled){
43597             return;
43598         }
43599         if(this.isExpanded()){
43600             this.collapse();
43601             if (!this.blockFocus) {
43602                 this.el.focus();
43603             }
43604             
43605         }else {
43606             this.hasFocus = true;
43607             if(this.triggerAction == 'all') {
43608                 this.doQuery(this.allQuery, true);
43609             } else {
43610                 this.doQuery(this.getRawValue());
43611             }
43612             if (!this.blockFocus) {
43613                 this.el.focus();
43614             }
43615         }
43616     },
43617     listKeyPress : function(e)
43618     {
43619         //Roo.log('listkeypress');
43620         // scroll to first matching element based on key pres..
43621         if (e.isSpecialKey()) {
43622             return false;
43623         }
43624         var k = String.fromCharCode(e.getKey()).toUpperCase();
43625         //Roo.log(k);
43626         var match  = false;
43627         var csel = this.view.getSelectedNodes();
43628         var cselitem = false;
43629         if (csel.length) {
43630             var ix = this.view.indexOf(csel[0]);
43631             cselitem  = this.store.getAt(ix);
43632             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43633                 cselitem = false;
43634             }
43635             
43636         }
43637         
43638         this.store.each(function(v) { 
43639             if (cselitem) {
43640                 // start at existing selection.
43641                 if (cselitem.id == v.id) {
43642                     cselitem = false;
43643                 }
43644                 return;
43645             }
43646                 
43647             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43648                 match = this.store.indexOf(v);
43649                 return false;
43650             }
43651         }, this);
43652         
43653         if (match === false) {
43654             return true; // no more action?
43655         }
43656         // scroll to?
43657         this.view.select(match);
43658         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43659         sn.scrollIntoView(sn.dom.parentNode, false);
43660     } 
43661
43662     /** 
43663     * @cfg {Boolean} grow 
43664     * @hide 
43665     */
43666     /** 
43667     * @cfg {Number} growMin 
43668     * @hide 
43669     */
43670     /** 
43671     * @cfg {Number} growMax 
43672     * @hide 
43673     */
43674     /**
43675      * @hide
43676      * @method autoSize
43677      */
43678 });/*
43679  * Copyright(c) 2010-2012, Roo J Solutions Limited
43680  *
43681  * Licence LGPL
43682  *
43683  */
43684
43685 /**
43686  * @class Roo.form.ComboBoxArray
43687  * @extends Roo.form.TextField
43688  * A facebook style adder... for lists of email / people / countries  etc...
43689  * pick multiple items from a combo box, and shows each one.
43690  *
43691  *  Fred [x]  Brian [x]  [Pick another |v]
43692  *
43693  *
43694  *  For this to work: it needs various extra information
43695  *    - normal combo problay has
43696  *      name, hiddenName
43697  *    + displayField, valueField
43698  *
43699  *    For our purpose...
43700  *
43701  *
43702  *   If we change from 'extends' to wrapping...
43703  *   
43704  *  
43705  *
43706  
43707  
43708  * @constructor
43709  * Create a new ComboBoxArray.
43710  * @param {Object} config Configuration options
43711  */
43712  
43713
43714 Roo.form.ComboBoxArray = function(config)
43715 {
43716     this.addEvents({
43717         /**
43718          * @event beforeremove
43719          * Fires before remove the value from the list
43720              * @param {Roo.form.ComboBoxArray} _self This combo box array
43721              * @param {Roo.form.ComboBoxArray.Item} item removed item
43722              */
43723         'beforeremove' : true,
43724         /**
43725          * @event remove
43726          * Fires when remove the value from the list
43727              * @param {Roo.form.ComboBoxArray} _self This combo box array
43728              * @param {Roo.form.ComboBoxArray.Item} item removed item
43729              */
43730         'remove' : true
43731         
43732         
43733     });
43734     
43735     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43736     
43737     this.items = new Roo.util.MixedCollection(false);
43738     
43739     // construct the child combo...
43740     
43741     
43742     
43743     
43744    
43745     
43746 }
43747
43748  
43749 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43750
43751     /**
43752      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43753      */
43754     
43755     lastData : false,
43756     
43757     // behavies liek a hiddne field
43758     inputType:      'hidden',
43759     /**
43760      * @cfg {Number} width The width of the box that displays the selected element
43761      */ 
43762     width:          300,
43763
43764     
43765     
43766     /**
43767      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43768      */
43769     name : false,
43770     /**
43771      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43772      */
43773     hiddenName : false,
43774       /**
43775      * @cfg {String} seperator    The value seperator normally ',' 
43776      */
43777     seperator : ',',
43778     
43779     // private the array of items that are displayed..
43780     items  : false,
43781     // private - the hidden field el.
43782     hiddenEl : false,
43783     // private - the filed el..
43784     el : false,
43785     
43786     //validateValue : function() { return true; }, // all values are ok!
43787     //onAddClick: function() { },
43788     
43789     onRender : function(ct, position) 
43790     {
43791         
43792         // create the standard hidden element
43793         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43794         
43795         
43796         // give fake names to child combo;
43797         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43798         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43799         
43800         this.combo = Roo.factory(this.combo, Roo.form);
43801         this.combo.onRender(ct, position);
43802         if (typeof(this.combo.width) != 'undefined') {
43803             this.combo.onResize(this.combo.width,0);
43804         }
43805         
43806         this.combo.initEvents();
43807         
43808         // assigned so form know we need to do this..
43809         this.store          = this.combo.store;
43810         this.valueField     = this.combo.valueField;
43811         this.displayField   = this.combo.displayField ;
43812         
43813         
43814         this.combo.wrap.addClass('x-cbarray-grp');
43815         
43816         var cbwrap = this.combo.wrap.createChild(
43817             {tag: 'div', cls: 'x-cbarray-cb'},
43818             this.combo.el.dom
43819         );
43820         
43821              
43822         this.hiddenEl = this.combo.wrap.createChild({
43823             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43824         });
43825         this.el = this.combo.wrap.createChild({
43826             tag: 'input',  type:'hidden' , name: this.name, value : ''
43827         });
43828          //   this.el.dom.removeAttribute("name");
43829         
43830         
43831         this.outerWrap = this.combo.wrap;
43832         this.wrap = cbwrap;
43833         
43834         this.outerWrap.setWidth(this.width);
43835         this.outerWrap.dom.removeChild(this.el.dom);
43836         
43837         this.wrap.dom.appendChild(this.el.dom);
43838         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43839         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43840         
43841         this.combo.trigger.setStyle('position','relative');
43842         this.combo.trigger.setStyle('left', '0px');
43843         this.combo.trigger.setStyle('top', '2px');
43844         
43845         this.combo.el.setStyle('vertical-align', 'text-bottom');
43846         
43847         //this.trigger.setStyle('vertical-align', 'top');
43848         
43849         // this should use the code from combo really... on('add' ....)
43850         if (this.adder) {
43851             
43852         
43853             this.adder = this.outerWrap.createChild(
43854                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43855             var _t = this;
43856             this.adder.on('click', function(e) {
43857                 _t.fireEvent('adderclick', this, e);
43858             }, _t);
43859         }
43860         //var _t = this;
43861         //this.adder.on('click', this.onAddClick, _t);
43862         
43863         
43864         this.combo.on('select', function(cb, rec, ix) {
43865             this.addItem(rec.data);
43866             
43867             cb.setValue('');
43868             cb.el.dom.value = '';
43869             //cb.lastData = rec.data;
43870             // add to list
43871             
43872         }, this);
43873         
43874         
43875     },
43876     
43877     
43878     getName: function()
43879     {
43880         // returns hidden if it's set..
43881         if (!this.rendered) {return ''};
43882         return  this.hiddenName ? this.hiddenName : this.name;
43883         
43884     },
43885     
43886     
43887     onResize: function(w, h){
43888         
43889         return;
43890         // not sure if this is needed..
43891         //this.combo.onResize(w,h);
43892         
43893         if(typeof w != 'number'){
43894             // we do not handle it!?!?
43895             return;
43896         }
43897         var tw = this.combo.trigger.getWidth();
43898         tw += this.addicon ? this.addicon.getWidth() : 0;
43899         tw += this.editicon ? this.editicon.getWidth() : 0;
43900         var x = w - tw;
43901         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43902             
43903         this.combo.trigger.setStyle('left', '0px');
43904         
43905         if(this.list && this.listWidth === undefined){
43906             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43907             this.list.setWidth(lw);
43908             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43909         }
43910         
43911     
43912         
43913     },
43914     
43915     addItem: function(rec)
43916     {
43917         var valueField = this.combo.valueField;
43918         var displayField = this.combo.displayField;
43919         
43920         if (this.items.indexOfKey(rec[valueField]) > -1) {
43921             //console.log("GOT " + rec.data.id);
43922             return;
43923         }
43924         
43925         var x = new Roo.form.ComboBoxArray.Item({
43926             //id : rec[this.idField],
43927             data : rec,
43928             displayField : displayField ,
43929             tipField : displayField ,
43930             cb : this
43931         });
43932         // use the 
43933         this.items.add(rec[valueField],x);
43934         // add it before the element..
43935         this.updateHiddenEl();
43936         x.render(this.outerWrap, this.wrap.dom);
43937         // add the image handler..
43938     },
43939     
43940     updateHiddenEl : function()
43941     {
43942         this.validate();
43943         if (!this.hiddenEl) {
43944             return;
43945         }
43946         var ar = [];
43947         var idField = this.combo.valueField;
43948         
43949         this.items.each(function(f) {
43950             ar.push(f.data[idField]);
43951         });
43952         this.hiddenEl.dom.value = ar.join(this.seperator);
43953         this.validate();
43954     },
43955     
43956     reset : function()
43957     {
43958         this.items.clear();
43959         
43960         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43961            el.remove();
43962         });
43963         
43964         this.el.dom.value = '';
43965         if (this.hiddenEl) {
43966             this.hiddenEl.dom.value = '';
43967         }
43968         
43969     },
43970     getValue: function()
43971     {
43972         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43973     },
43974     setValue: function(v) // not a valid action - must use addItems..
43975     {
43976         
43977         this.reset();
43978          
43979         if (this.store.isLocal && (typeof(v) == 'string')) {
43980             // then we can use the store to find the values..
43981             // comma seperated at present.. this needs to allow JSON based encoding..
43982             this.hiddenEl.value  = v;
43983             var v_ar = [];
43984             Roo.each(v.split(this.seperator), function(k) {
43985                 Roo.log("CHECK " + this.valueField + ',' + k);
43986                 var li = this.store.query(this.valueField, k);
43987                 if (!li.length) {
43988                     return;
43989                 }
43990                 var add = {};
43991                 add[this.valueField] = k;
43992                 add[this.displayField] = li.item(0).data[this.displayField];
43993                 
43994                 this.addItem(add);
43995             }, this) 
43996              
43997         }
43998         if (typeof(v) == 'object' ) {
43999             // then let's assume it's an array of objects..
44000             Roo.each(v, function(l) {
44001                 var add = l;
44002                 if (typeof(l) == 'string') {
44003                     add = {};
44004                     add[this.valueField] = l;
44005                     add[this.displayField] = l
44006                 }
44007                 this.addItem(add);
44008             }, this);
44009              
44010         }
44011         
44012         
44013     },
44014     setFromData: function(v)
44015     {
44016         // this recieves an object, if setValues is called.
44017         this.reset();
44018         this.el.dom.value = v[this.displayField];
44019         this.hiddenEl.dom.value = v[this.valueField];
44020         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
44021             return;
44022         }
44023         var kv = v[this.valueField];
44024         var dv = v[this.displayField];
44025         kv = typeof(kv) != 'string' ? '' : kv;
44026         dv = typeof(dv) != 'string' ? '' : dv;
44027         
44028         
44029         var keys = kv.split(this.seperator);
44030         var display = dv.split(this.seperator);
44031         for (var i = 0 ; i < keys.length; i++) {
44032             add = {};
44033             add[this.valueField] = keys[i];
44034             add[this.displayField] = display[i];
44035             this.addItem(add);
44036         }
44037       
44038         
44039     },
44040     
44041     /**
44042      * Validates the combox array value
44043      * @return {Boolean} True if the value is valid, else false
44044      */
44045     validate : function(){
44046         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
44047             this.clearInvalid();
44048             return true;
44049         }
44050         return false;
44051     },
44052     
44053     validateValue : function(value){
44054         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
44055         
44056     },
44057     
44058     /*@
44059      * overide
44060      * 
44061      */
44062     isDirty : function() {
44063         if(this.disabled) {
44064             return false;
44065         }
44066         
44067         try {
44068             var d = Roo.decode(String(this.originalValue));
44069         } catch (e) {
44070             return String(this.getValue()) !== String(this.originalValue);
44071         }
44072         
44073         var originalValue = [];
44074         
44075         for (var i = 0; i < d.length; i++){
44076             originalValue.push(d[i][this.valueField]);
44077         }
44078         
44079         return String(this.getValue()) !== String(originalValue.join(this.seperator));
44080         
44081     }
44082     
44083 });
44084
44085
44086
44087 /**
44088  * @class Roo.form.ComboBoxArray.Item
44089  * @extends Roo.BoxComponent
44090  * A selected item in the list
44091  *  Fred [x]  Brian [x]  [Pick another |v]
44092  * 
44093  * @constructor
44094  * Create a new item.
44095  * @param {Object} config Configuration options
44096  */
44097  
44098 Roo.form.ComboBoxArray.Item = function(config) {
44099     config.id = Roo.id();
44100     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
44101 }
44102
44103 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
44104     data : {},
44105     cb: false,
44106     displayField : false,
44107     tipField : false,
44108     
44109     
44110     defaultAutoCreate : {
44111         tag: 'div',
44112         cls: 'x-cbarray-item',
44113         cn : [ 
44114             { tag: 'div' },
44115             {
44116                 tag: 'img',
44117                 width:16,
44118                 height : 16,
44119                 src : Roo.BLANK_IMAGE_URL ,
44120                 align: 'center'
44121             }
44122         ]
44123         
44124     },
44125     
44126  
44127     onRender : function(ct, position)
44128     {
44129         Roo.form.Field.superclass.onRender.call(this, ct, position);
44130         
44131         if(!this.el){
44132             var cfg = this.getAutoCreate();
44133             this.el = ct.createChild(cfg, position);
44134         }
44135         
44136         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
44137         
44138         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
44139             this.cb.renderer(this.data) :
44140             String.format('{0}',this.data[this.displayField]);
44141         
44142             
44143         this.el.child('div').dom.setAttribute('qtip',
44144                         String.format('{0}',this.data[this.tipField])
44145         );
44146         
44147         this.el.child('img').on('click', this.remove, this);
44148         
44149     },
44150    
44151     remove : function()
44152     {
44153         if(this.cb.disabled){
44154             return;
44155         }
44156         
44157         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
44158             this.cb.items.remove(this);
44159             this.el.child('img').un('click', this.remove, this);
44160             this.el.remove();
44161             this.cb.updateHiddenEl();
44162
44163             this.cb.fireEvent('remove', this.cb, this);
44164         }
44165         
44166     }
44167 });/*
44168  * RooJS Library 1.1.1
44169  * Copyright(c) 2008-2011  Alan Knowles
44170  *
44171  * License - LGPL
44172  */
44173  
44174
44175 /**
44176  * @class Roo.form.ComboNested
44177  * @extends Roo.form.ComboBox
44178  * A combobox for that allows selection of nested items in a list,
44179  * eg.
44180  *
44181  *  Book
44182  *    -> red
44183  *    -> green
44184  *  Table
44185  *    -> square
44186  *      ->red
44187  *      ->green
44188  *    -> rectangle
44189  *      ->green
44190  *      
44191  * 
44192  * @constructor
44193  * Create a new ComboNested
44194  * @param {Object} config Configuration options
44195  */
44196 Roo.form.ComboNested = function(config){
44197     Roo.form.ComboCheck.superclass.constructor.call(this, config);
44198     // should verify some data...
44199     // like
44200     // hiddenName = required..
44201     // displayField = required
44202     // valudField == required
44203     var req= [ 'hiddenName', 'displayField', 'valueField' ];
44204     var _t = this;
44205     Roo.each(req, function(e) {
44206         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
44207             throw "Roo.form.ComboNested : missing value for: " + e;
44208         }
44209     });
44210      
44211     
44212 };
44213
44214 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
44215    
44216     /*
44217      * @config {Number} max Number of columns to show
44218      */
44219     
44220     maxColumns : 3,
44221    
44222     list : null, // the outermost div..
44223     innerLists : null, // the
44224     views : null,
44225     stores : null,
44226     // private
44227     loadingChildren : false,
44228     
44229     onRender : function(ct, position)
44230     {
44231         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
44232         
44233         if(this.hiddenName){
44234             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
44235                     'before', true);
44236             this.hiddenField.value =
44237                 this.hiddenValue !== undefined ? this.hiddenValue :
44238                 this.value !== undefined ? this.value : '';
44239
44240             // prevent input submission
44241             this.el.dom.removeAttribute('name');
44242              
44243              
44244         }
44245         
44246         if(Roo.isGecko){
44247             this.el.dom.setAttribute('autocomplete', 'off');
44248         }
44249
44250         var cls = 'x-combo-list';
44251
44252         this.list = new Roo.Layer({
44253             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
44254         });
44255
44256         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
44257         this.list.setWidth(lw);
44258         this.list.swallowEvent('mousewheel');
44259         this.assetHeight = 0;
44260
44261         if(this.title){
44262             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
44263             this.assetHeight += this.header.getHeight();
44264         }
44265         this.innerLists = [];
44266         this.views = [];
44267         this.stores = [];
44268         for (var i =0 ; i < this.maxColumns; i++) {
44269             this.onRenderList( cls, i);
44270         }
44271         
44272         // always needs footer, as we are going to have an 'OK' button.
44273         this.footer = this.list.createChild({cls:cls+'-ft'});
44274         this.pageTb = new Roo.Toolbar(this.footer);  
44275         var _this = this;
44276         this.pageTb.add(  {
44277             
44278             text: 'Done',
44279             handler: function()
44280             {
44281                 _this.collapse();
44282             }
44283         });
44284         
44285         if ( this.allowBlank && !this.disableClear) {
44286             
44287             this.pageTb.add(new Roo.Toolbar.Fill(), {
44288                 cls: 'x-btn-icon x-btn-clear',
44289                 text: '&#160;',
44290                 handler: function()
44291                 {
44292                     _this.collapse();
44293                     _this.clearValue();
44294                     _this.onSelect(false, -1);
44295                 }
44296             });
44297         }
44298         if (this.footer) {
44299             this.assetHeight += this.footer.getHeight();
44300         }
44301         
44302     },
44303     onRenderList : function (  cls, i)
44304     {
44305         
44306         var lw = Math.floor(
44307                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44308         );
44309         
44310         this.list.setWidth(lw); // default to '1'
44311
44312         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
44313         //il.on('mouseover', this.onViewOver, this, { list:  i });
44314         //il.on('mousemove', this.onViewMove, this, { list:  i });
44315         il.setWidth(lw);
44316         il.setStyle({ 'overflow-x' : 'hidden'});
44317
44318         if(!this.tpl){
44319             this.tpl = new Roo.Template({
44320                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
44321                 isEmpty: function (value, allValues) {
44322                     //Roo.log(value);
44323                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
44324                     return dl ? 'has-children' : 'no-children'
44325                 }
44326             });
44327         }
44328         
44329         var store  = this.store;
44330         if (i > 0) {
44331             store  = new Roo.data.SimpleStore({
44332                 //fields : this.store.reader.meta.fields,
44333                 reader : this.store.reader,
44334                 data : [ ]
44335             });
44336         }
44337         this.stores[i]  = store;
44338                   
44339         var view = this.views[i] = new Roo.View(
44340             il,
44341             this.tpl,
44342             {
44343                 singleSelect:true,
44344                 store: store,
44345                 selectedClass: this.selectedClass
44346             }
44347         );
44348         view.getEl().setWidth(lw);
44349         view.getEl().setStyle({
44350             position: i < 1 ? 'relative' : 'absolute',
44351             top: 0,
44352             left: (i * lw ) + 'px',
44353             display : i > 0 ? 'none' : 'block'
44354         });
44355         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
44356         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
44357         //view.on('click', this.onViewClick, this, { list : i });
44358
44359         store.on('beforeload', this.onBeforeLoad, this);
44360         store.on('load',  this.onLoad, this, { list  : i});
44361         store.on('loadexception', this.onLoadException, this);
44362
44363         // hide the other vies..
44364         
44365         
44366         
44367     },
44368       
44369     restrictHeight : function()
44370     {
44371         var mh = 0;
44372         Roo.each(this.innerLists, function(il,i) {
44373             var el = this.views[i].getEl();
44374             el.dom.style.height = '';
44375             var inner = el.dom;
44376             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44377             // only adjust heights on other ones..
44378             mh = Math.max(h, mh);
44379             if (i < 1) {
44380                 
44381                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44382                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44383                
44384             }
44385             
44386             
44387         }, this);
44388         
44389         this.list.beginUpdate();
44390         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44391         this.list.alignTo(this.el, this.listAlign);
44392         this.list.endUpdate();
44393         
44394     },
44395      
44396     
44397     // -- store handlers..
44398     // private
44399     onBeforeLoad : function()
44400     {
44401         if(!this.hasFocus){
44402             return;
44403         }
44404         this.innerLists[0].update(this.loadingText ?
44405                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44406         this.restrictHeight();
44407         this.selectedIndex = -1;
44408     },
44409     // private
44410     onLoad : function(a,b,c,d)
44411     {
44412         if (!this.loadingChildren) {
44413             // then we are loading the top level. - hide the children
44414             for (var i = 1;i < this.views.length; i++) {
44415                 this.views[i].getEl().setStyle({ display : 'none' });
44416             }
44417             var lw = Math.floor(
44418                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44419             );
44420         
44421              this.list.setWidth(lw); // default to '1'
44422
44423             
44424         }
44425         if(!this.hasFocus){
44426             return;
44427         }
44428         
44429         if(this.store.getCount() > 0) {
44430             this.expand();
44431             this.restrictHeight();   
44432         } else {
44433             this.onEmptyResults();
44434         }
44435         
44436         if (!this.loadingChildren) {
44437             this.selectActive();
44438         }
44439         /*
44440         this.stores[1].loadData([]);
44441         this.stores[2].loadData([]);
44442         this.views
44443         */    
44444     
44445         //this.el.focus();
44446     },
44447     
44448     
44449     // private
44450     onLoadException : function()
44451     {
44452         this.collapse();
44453         Roo.log(this.store.reader.jsonData);
44454         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44455             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44456         }
44457         
44458         
44459     },
44460     // no cleaning of leading spaces on blur here.
44461     cleanLeadingSpace : function(e) { },
44462     
44463
44464     onSelectChange : function (view, sels, opts )
44465     {
44466         var ix = view.getSelectedIndexes();
44467          
44468         if (opts.list > this.maxColumns - 2) {
44469             if (view.store.getCount()<  1) {
44470                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44471
44472             } else  {
44473                 if (ix.length) {
44474                     // used to clear ?? but if we are loading unselected 
44475                     this.setFromData(view.store.getAt(ix[0]).data);
44476                 }
44477                 
44478             }
44479             
44480             return;
44481         }
44482         
44483         if (!ix.length) {
44484             // this get's fired when trigger opens..
44485            // this.setFromData({});
44486             var str = this.stores[opts.list+1];
44487             str.data.clear(); // removeall wihtout the fire events..
44488             return;
44489         }
44490         
44491         var rec = view.store.getAt(ix[0]);
44492          
44493         this.setFromData(rec.data);
44494         this.fireEvent('select', this, rec, ix[0]);
44495         
44496         var lw = Math.floor(
44497              (
44498                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44499              ) / this.maxColumns
44500         );
44501         this.loadingChildren = true;
44502         this.stores[opts.list+1].loadDataFromChildren( rec );
44503         this.loadingChildren = false;
44504         var dl = this.stores[opts.list+1]. getTotalCount();
44505         
44506         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44507         
44508         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44509         for (var i = opts.list+2; i < this.views.length;i++) {
44510             this.views[i].getEl().setStyle({ display : 'none' });
44511         }
44512         
44513         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44514         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44515         
44516         if (this.isLoading) {
44517            // this.selectActive(opts.list);
44518         }
44519          
44520     },
44521     
44522     
44523     
44524     
44525     onDoubleClick : function()
44526     {
44527         this.collapse(); //??
44528     },
44529     
44530      
44531     
44532     
44533     
44534     // private
44535     recordToStack : function(store, prop, value, stack)
44536     {
44537         var cstore = new Roo.data.SimpleStore({
44538             //fields : this.store.reader.meta.fields, // we need array reader.. for
44539             reader : this.store.reader,
44540             data : [ ]
44541         });
44542         var _this = this;
44543         var record  = false;
44544         var srec = false;
44545         if(store.getCount() < 1){
44546             return false;
44547         }
44548         store.each(function(r){
44549             if(r.data[prop] == value){
44550                 record = r;
44551             srec = r;
44552                 return false;
44553             }
44554             if (r.data.cn && r.data.cn.length) {
44555                 cstore.loadDataFromChildren( r);
44556                 var cret = _this.recordToStack(cstore, prop, value, stack);
44557                 if (cret !== false) {
44558                     record = cret;
44559                     srec = r;
44560                     return false;
44561                 }
44562             }
44563              
44564             return true;
44565         });
44566         if (record == false) {
44567             return false
44568         }
44569         stack.unshift(srec);
44570         return record;
44571     },
44572     
44573     /*
44574      * find the stack of stores that match our value.
44575      *
44576      * 
44577      */
44578     
44579     selectActive : function ()
44580     {
44581         // if store is not loaded, then we will need to wait for that to happen first.
44582         var stack = [];
44583         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44584         for (var i = 0; i < stack.length; i++ ) {
44585             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44586         }
44587         
44588     }
44589         
44590          
44591     
44592     
44593     
44594     
44595 });/*
44596  * Based on:
44597  * Ext JS Library 1.1.1
44598  * Copyright(c) 2006-2007, Ext JS, LLC.
44599  *
44600  * Originally Released Under LGPL - original licence link has changed is not relivant.
44601  *
44602  * Fork - LGPL
44603  * <script type="text/javascript">
44604  */
44605 /**
44606  * @class Roo.form.Checkbox
44607  * @extends Roo.form.Field
44608  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44609  * @constructor
44610  * Creates a new Checkbox
44611  * @param {Object} config Configuration options
44612  */
44613 Roo.form.Checkbox = function(config){
44614     Roo.form.Checkbox.superclass.constructor.call(this, config);
44615     this.addEvents({
44616         /**
44617          * @event check
44618          * Fires when the checkbox is checked or unchecked.
44619              * @param {Roo.form.Checkbox} this This checkbox
44620              * @param {Boolean} checked The new checked value
44621              */
44622         check : true
44623     });
44624 };
44625
44626 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44627     /**
44628      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44629      */
44630     focusClass : undefined,
44631     /**
44632      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44633      */
44634     fieldClass: "x-form-field",
44635     /**
44636      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44637      */
44638     checked: false,
44639     /**
44640      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44641      * {tag: "input", type: "checkbox", autocomplete: "off"})
44642      */
44643     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44644     /**
44645      * @cfg {String} boxLabel The text that appears beside the checkbox
44646      */
44647     boxLabel : "",
44648     /**
44649      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44650      */  
44651     inputValue : '1',
44652     /**
44653      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44654      */
44655      valueOff: '0', // value when not checked..
44656
44657     actionMode : 'viewEl', 
44658     //
44659     // private
44660     itemCls : 'x-menu-check-item x-form-item',
44661     groupClass : 'x-menu-group-item',
44662     inputType : 'hidden',
44663     
44664     
44665     inSetChecked: false, // check that we are not calling self...
44666     
44667     inputElement: false, // real input element?
44668     basedOn: false, // ????
44669     
44670     isFormField: true, // not sure where this is needed!!!!
44671
44672     onResize : function(){
44673         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44674         if(!this.boxLabel){
44675             this.el.alignTo(this.wrap, 'c-c');
44676         }
44677     },
44678
44679     initEvents : function(){
44680         Roo.form.Checkbox.superclass.initEvents.call(this);
44681         this.el.on("click", this.onClick,  this);
44682         this.el.on("change", this.onClick,  this);
44683     },
44684
44685
44686     getResizeEl : function(){
44687         return this.wrap;
44688     },
44689
44690     getPositionEl : function(){
44691         return this.wrap;
44692     },
44693
44694     // private
44695     onRender : function(ct, position){
44696         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44697         /*
44698         if(this.inputValue !== undefined){
44699             this.el.dom.value = this.inputValue;
44700         }
44701         */
44702         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44703         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44704         var viewEl = this.wrap.createChild({ 
44705             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44706         this.viewEl = viewEl;   
44707         this.wrap.on('click', this.onClick,  this); 
44708         
44709         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44710         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44711         
44712         
44713         
44714         if(this.boxLabel){
44715             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44716         //    viewEl.on('click', this.onClick,  this); 
44717         }
44718         //if(this.checked){
44719             this.setChecked(this.checked);
44720         //}else{
44721             //this.checked = this.el.dom;
44722         //}
44723
44724     },
44725
44726     // private
44727     initValue : Roo.emptyFn,
44728
44729     /**
44730      * Returns the checked state of the checkbox.
44731      * @return {Boolean} True if checked, else false
44732      */
44733     getValue : function(){
44734         if(this.el){
44735             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44736         }
44737         return this.valueOff;
44738         
44739     },
44740
44741         // private
44742     onClick : function(){ 
44743         if (this.disabled) {
44744             return;
44745         }
44746         this.setChecked(!this.checked);
44747
44748         //if(this.el.dom.checked != this.checked){
44749         //    this.setValue(this.el.dom.checked);
44750        // }
44751     },
44752
44753     /**
44754      * Sets the checked state of the checkbox.
44755      * On is always based on a string comparison between inputValue and the param.
44756      * @param {Boolean/String} value - the value to set 
44757      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44758      */
44759     setValue : function(v,suppressEvent){
44760         
44761         
44762         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44763         //if(this.el && this.el.dom){
44764         //    this.el.dom.checked = this.checked;
44765         //    this.el.dom.defaultChecked = this.checked;
44766         //}
44767         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44768         //this.fireEvent("check", this, this.checked);
44769     },
44770     // private..
44771     setChecked : function(state,suppressEvent)
44772     {
44773         if (this.inSetChecked) {
44774             this.checked = state;
44775             return;
44776         }
44777         
44778     
44779         if(this.wrap){
44780             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44781         }
44782         this.checked = state;
44783         if(suppressEvent !== true){
44784             this.fireEvent('check', this, state);
44785         }
44786         this.inSetChecked = true;
44787         this.el.dom.value = state ? this.inputValue : this.valueOff;
44788         this.inSetChecked = false;
44789         
44790     },
44791     // handle setting of hidden value by some other method!!?!?
44792     setFromHidden: function()
44793     {
44794         if(!this.el){
44795             return;
44796         }
44797         //console.log("SET FROM HIDDEN");
44798         //alert('setFrom hidden');
44799         this.setValue(this.el.dom.value);
44800     },
44801     
44802     onDestroy : function()
44803     {
44804         if(this.viewEl){
44805             Roo.get(this.viewEl).remove();
44806         }
44807          
44808         Roo.form.Checkbox.superclass.onDestroy.call(this);
44809     },
44810     
44811     setBoxLabel : function(str)
44812     {
44813         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44814     }
44815
44816 });/*
44817  * Based on:
44818  * Ext JS Library 1.1.1
44819  * Copyright(c) 2006-2007, Ext JS, LLC.
44820  *
44821  * Originally Released Under LGPL - original licence link has changed is not relivant.
44822  *
44823  * Fork - LGPL
44824  * <script type="text/javascript">
44825  */
44826  
44827 /**
44828  * @class Roo.form.Radio
44829  * @extends Roo.form.Checkbox
44830  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44831  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44832  * @constructor
44833  * Creates a new Radio
44834  * @param {Object} config Configuration options
44835  */
44836 Roo.form.Radio = function(){
44837     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44838 };
44839 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44840     inputType: 'radio',
44841
44842     /**
44843      * If this radio is part of a group, it will return the selected value
44844      * @return {String}
44845      */
44846     getGroupValue : function(){
44847         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44848     },
44849     
44850     
44851     onRender : function(ct, position){
44852         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44853         
44854         if(this.inputValue !== undefined){
44855             this.el.dom.value = this.inputValue;
44856         }
44857          
44858         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44859         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44860         //var viewEl = this.wrap.createChild({ 
44861         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44862         //this.viewEl = viewEl;   
44863         //this.wrap.on('click', this.onClick,  this); 
44864         
44865         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44866         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44867         
44868         
44869         
44870         if(this.boxLabel){
44871             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44872         //    viewEl.on('click', this.onClick,  this); 
44873         }
44874          if(this.checked){
44875             this.el.dom.checked =   'checked' ;
44876         }
44877          
44878     } 
44879     
44880     
44881 });Roo.rtf = {}; // namespace
44882 Roo.rtf.Hex = function(hex)
44883 {
44884     this.hexstr = hex;
44885 };
44886 Roo.rtf.Paragraph = function(opts)
44887 {
44888     this.content = []; ///??? is that used?
44889 };Roo.rtf.Span = function(opts)
44890 {
44891     this.value = opts.value;
44892 };
44893
44894 Roo.rtf.Group = function(parent)
44895 {
44896     // we dont want to acutally store parent - it will make debug a nightmare..
44897     this.content = [];
44898     this.cn  = [];
44899      
44900        
44901     
44902 };
44903
44904 Roo.rtf.Group.prototype = {
44905     ignorable : false,
44906     content: false,
44907     cn: false,
44908     addContent : function(node) {
44909         // could set styles...
44910         this.content.push(node);
44911     },
44912     addChild : function(cn)
44913     {
44914         this.cn.push(cn);
44915     },
44916     // only for images really...
44917     toDataURL : function()
44918     {
44919         var mimetype = false;
44920         switch(true) {
44921             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44922                 mimetype = "image/png";
44923                 break;
44924              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44925                 mimetype = "image/jpeg";
44926                 break;
44927             default :
44928                 return 'about:blank'; // ?? error?
44929         }
44930         
44931         
44932         var hexstring = this.content[this.content.length-1].value;
44933         
44934         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44935             return String.fromCharCode(parseInt(a, 16));
44936         }).join(""));
44937     }
44938     
44939 };
44940 // this looks like it's normally the {rtf{ .... }}
44941 Roo.rtf.Document = function()
44942 {
44943     // we dont want to acutally store parent - it will make debug a nightmare..
44944     this.rtlch  = [];
44945     this.content = [];
44946     this.cn = [];
44947     
44948 };
44949 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44950     addChild : function(cn)
44951     {
44952         this.cn.push(cn);
44953         switch(cn.type) {
44954             case 'rtlch': // most content seems to be inside this??
44955             case 'listtext':
44956             case 'shpinst':
44957                 this.rtlch.push(cn);
44958                 return;
44959             default:
44960                 this[cn.type] = cn;
44961         }
44962         
44963     },
44964     
44965     getElementsByType : function(type)
44966     {
44967         var ret =  [];
44968         this._getElementsByType(type, ret, this.cn, 'rtf');
44969         return ret;
44970     },
44971     _getElementsByType : function (type, ret, search_array, path)
44972     {
44973         search_array.forEach(function(n,i) {
44974             if (n.type == type) {
44975                 n.path = path + '/' + n.type + ':' + i;
44976                 ret.push(n);
44977             }
44978             if (n.cn.length > 0) {
44979                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44980             }
44981         },this);
44982     }
44983     
44984 });
44985  
44986 Roo.rtf.Ctrl = function(opts)
44987 {
44988     this.value = opts.value;
44989     this.param = opts.param;
44990 };
44991 /**
44992  *
44993  *
44994  * based on this https://github.com/iarna/rtf-parser
44995  * it's really only designed to extract pict from pasted RTF 
44996  *
44997  * usage:
44998  *
44999  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
45000  *  
45001  *
45002  */
45003
45004  
45005
45006
45007
45008 Roo.rtf.Parser = function(text) {
45009     //super({objectMode: true})
45010     this.text = '';
45011     this.parserState = this.parseText;
45012     
45013     // these are for interpeter...
45014     this.doc = {};
45015     ///this.parserState = this.parseTop
45016     this.groupStack = [];
45017     this.hexStore = [];
45018     this.doc = false;
45019     
45020     this.groups = []; // where we put the return.
45021     
45022     for (var ii = 0; ii < text.length; ++ii) {
45023         ++this.cpos;
45024         
45025         if (text[ii] === '\n') {
45026             ++this.row;
45027             this.col = 1;
45028         } else {
45029             ++this.col;
45030         }
45031         this.parserState(text[ii]);
45032     }
45033     
45034     
45035     
45036 };
45037 Roo.rtf.Parser.prototype = {
45038     text : '', // string being parsed..
45039     controlWord : '',
45040     controlWordParam :  '',
45041     hexChar : '',
45042     doc : false,
45043     group: false,
45044     groupStack : false,
45045     hexStore : false,
45046     
45047     
45048     cpos : 0, 
45049     row : 1, // reportin?
45050     col : 1, //
45051
45052      
45053     push : function (el)
45054     {
45055         var m = 'cmd'+ el.type;
45056         if (typeof(this[m]) == 'undefined') {
45057             Roo.log('invalid cmd:' + el.type);
45058             return;
45059         }
45060         this[m](el);
45061         //Roo.log(el);
45062     },
45063     flushHexStore : function()
45064     {
45065         if (this.hexStore.length < 1) {
45066             return;
45067         }
45068         var hexstr = this.hexStore.map(
45069             function(cmd) {
45070                 return cmd.value;
45071         }).join('');
45072         
45073         this.group.addContent( new Roo.rtf.Hex( hexstr ));
45074               
45075             
45076         this.hexStore.splice(0)
45077         
45078     },
45079     
45080     cmdgroupstart : function()
45081     {
45082         this.flushHexStore();
45083         if (this.group) {
45084             this.groupStack.push(this.group);
45085         }
45086          // parent..
45087         if (this.doc === false) {
45088             this.group = this.doc = new Roo.rtf.Document();
45089             return;
45090             
45091         }
45092         this.group = new Roo.rtf.Group(this.group);
45093     },
45094     cmdignorable : function()
45095     {
45096         this.flushHexStore();
45097         this.group.ignorable = true;
45098     },
45099     cmdendparagraph : function()
45100     {
45101         this.flushHexStore();
45102         this.group.addContent(new Roo.rtf.Paragraph());
45103     },
45104     cmdgroupend : function ()
45105     {
45106         this.flushHexStore();
45107         var endingGroup = this.group;
45108         
45109         
45110         this.group = this.groupStack.pop();
45111         if (this.group) {
45112             this.group.addChild(endingGroup);
45113         }
45114         
45115         
45116         
45117         var doc = this.group || this.doc;
45118         //if (endingGroup instanceof FontTable) {
45119         //  doc.fonts = endingGroup.table
45120         //} else if (endingGroup instanceof ColorTable) {
45121         //  doc.colors = endingGroup.table
45122         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
45123         if (endingGroup.ignorable === false) {
45124             //code
45125             this.groups.push(endingGroup);
45126            // Roo.log( endingGroup );
45127         }
45128             //Roo.each(endingGroup.content, function(item)) {
45129             //    doc.addContent(item);
45130             //}
45131             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
45132         //}
45133     },
45134     cmdtext : function (cmd)
45135     {
45136         this.flushHexStore();
45137         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
45138             //this.group = this.doc
45139         }
45140         this.group.addContent(new Roo.rtf.Span(cmd));
45141     },
45142     cmdcontrolword : function (cmd)
45143     {
45144         this.flushHexStore();
45145         if (!this.group.type) {
45146             this.group.type = cmd.value;
45147             return;
45148         }
45149         this.group.addContent(new Roo.rtf.Ctrl(cmd));
45150         // we actually don't care about ctrl words...
45151         return ;
45152         /*
45153         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
45154         if (this[method]) {
45155             this[method](cmd.param)
45156         } else {
45157             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
45158         }
45159         */
45160     },
45161     cmdhexchar : function(cmd) {
45162         this.hexStore.push(cmd);
45163     },
45164     cmderror : function(cmd) {
45165         throw new Exception (cmd.value);
45166     },
45167     
45168     /*
45169       _flush (done) {
45170         if (this.text !== '\u0000') this.emitText()
45171         done()
45172       }
45173       */
45174       
45175       
45176     parseText : function(c)
45177     {
45178         if (c === '\\') {
45179             this.parserState = this.parseEscapes;
45180         } else if (c === '{') {
45181             this.emitStartGroup();
45182         } else if (c === '}') {
45183             this.emitEndGroup();
45184         } else if (c === '\x0A' || c === '\x0D') {
45185             // cr/lf are noise chars
45186         } else {
45187             this.text += c;
45188         }
45189     },
45190     
45191     parseEscapes: function (c)
45192     {
45193         if (c === '\\' || c === '{' || c === '}') {
45194             this.text += c;
45195             this.parserState = this.parseText;
45196         } else {
45197             this.parserState = this.parseControlSymbol;
45198             this.parseControlSymbol(c);
45199         }
45200     },
45201     parseControlSymbol: function(c)
45202     {
45203         if (c === '~') {
45204             this.text += '\u00a0'; // nbsp
45205             this.parserState = this.parseText
45206         } else if (c === '-') {
45207              this.text += '\u00ad'; // soft hyphen
45208         } else if (c === '_') {
45209             this.text += '\u2011'; // non-breaking hyphen
45210         } else if (c === '*') {
45211             this.emitIgnorable();
45212             this.parserState = this.parseText;
45213         } else if (c === "'") {
45214             this.parserState = this.parseHexChar;
45215         } else if (c === '|') { // formula cacter
45216             this.emitFormula();
45217             this.parserState = this.parseText;
45218         } else if (c === ':') { // subentry in an index entry
45219             this.emitIndexSubEntry();
45220             this.parserState = this.parseText;
45221         } else if (c === '\x0a') {
45222             this.emitEndParagraph();
45223             this.parserState = this.parseText;
45224         } else if (c === '\x0d') {
45225             this.emitEndParagraph();
45226             this.parserState = this.parseText;
45227         } else {
45228             this.parserState = this.parseControlWord;
45229             this.parseControlWord(c);
45230         }
45231     },
45232     parseHexChar: function (c)
45233     {
45234         if (/^[A-Fa-f0-9]$/.test(c)) {
45235             this.hexChar += c;
45236             if (this.hexChar.length >= 2) {
45237               this.emitHexChar();
45238               this.parserState = this.parseText;
45239             }
45240             return;
45241         }
45242         this.emitError("Invalid character \"" + c + "\" in hex literal.");
45243         this.parserState = this.parseText;
45244         
45245     },
45246     parseControlWord : function(c)
45247     {
45248         if (c === ' ') {
45249             this.emitControlWord();
45250             this.parserState = this.parseText;
45251         } else if (/^[-\d]$/.test(c)) {
45252             this.parserState = this.parseControlWordParam;
45253             this.controlWordParam += c;
45254         } else if (/^[A-Za-z]$/.test(c)) {
45255           this.controlWord += c;
45256         } else {
45257           this.emitControlWord();
45258           this.parserState = this.parseText;
45259           this.parseText(c);
45260         }
45261     },
45262     parseControlWordParam : function (c) {
45263         if (/^\d$/.test(c)) {
45264           this.controlWordParam += c;
45265         } else if (c === ' ') {
45266           this.emitControlWord();
45267           this.parserState = this.parseText;
45268         } else {
45269           this.emitControlWord();
45270           this.parserState = this.parseText;
45271           this.parseText(c);
45272         }
45273     },
45274     
45275     
45276     
45277     
45278     emitText : function () {
45279         if (this.text === '') {
45280             return;
45281         }
45282         this.push({
45283             type: 'text',
45284             value: this.text,
45285             pos: this.cpos,
45286             row: this.row,
45287             col: this.col
45288         });
45289         this.text = ''
45290     },
45291     emitControlWord : function ()
45292     {
45293         this.emitText();
45294         if (this.controlWord === '') {
45295             this.emitError('empty control word');
45296         } else {
45297             this.push({
45298                   type: 'controlword',
45299                   value: this.controlWord,
45300                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
45301                   pos: this.cpos,
45302                   row: this.row,
45303                   col: this.col
45304             });
45305         }
45306         this.controlWord = '';
45307         this.controlWordParam = '';
45308     },
45309     emitStartGroup : function ()
45310     {
45311         this.emitText();
45312         this.push({
45313             type: 'groupstart',
45314             pos: this.cpos,
45315             row: this.row,
45316             col: this.col
45317         });
45318     },
45319     emitEndGroup : function ()
45320     {
45321         this.emitText();
45322         this.push({
45323             type: 'groupend',
45324             pos: this.cpos,
45325             row: this.row,
45326             col: this.col
45327         });
45328     },
45329     emitIgnorable : function ()
45330     {
45331         this.emitText();
45332         this.push({
45333             type: 'ignorable',
45334             pos: this.cpos,
45335             row: this.row,
45336             col: this.col
45337         });
45338     },
45339     emitHexChar : function ()
45340     {
45341         this.emitText();
45342         this.push({
45343             type: 'hexchar',
45344             value: this.hexChar,
45345             pos: this.cpos,
45346             row: this.row,
45347             col: this.col
45348         });
45349         this.hexChar = ''
45350     },
45351     emitError : function (message)
45352     {
45353       this.emitText();
45354       this.push({
45355             type: 'error',
45356             value: message,
45357             row: this.row,
45358             col: this.col,
45359             char: this.cpos //,
45360             //stack: new Error().stack
45361         });
45362     },
45363     emitEndParagraph : function () {
45364         this.emitText();
45365         this.push({
45366             type: 'endparagraph',
45367             pos: this.cpos,
45368             row: this.row,
45369             col: this.col
45370         });
45371     }
45372      
45373 } ;
45374 Roo.htmleditor = {};
45375  
45376 /**
45377  * @class Roo.htmleditor.Filter
45378  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45379  * @cfg {DomElement} node The node to iterate and filter
45380  * @cfg {boolean|String|Array} tag Tags to replace 
45381  * @constructor
45382  * Create a new Filter.
45383  * @param {Object} config Configuration options
45384  */
45385
45386
45387
45388 Roo.htmleditor.Filter = function(cfg) {
45389     Roo.apply(this.cfg);
45390     // this does not actually call walk as it's really just a abstract class
45391 }
45392
45393
45394 Roo.htmleditor.Filter.prototype = {
45395     
45396     node: false,
45397     
45398     tag: false,
45399
45400     // overrride to do replace comments.
45401     replaceComment : false,
45402     
45403     // overrride to do replace or do stuff with tags..
45404     replaceTag : false,
45405     
45406     walk : function(dom)
45407     {
45408         Roo.each( Array.from(dom.childNodes), function( e ) {
45409             switch(true) {
45410                 
45411                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45412                     this.replaceComment(e);
45413                     return;
45414                 
45415                 case e.nodeType != 1: //not a node.
45416                     return;
45417                 
45418                 case this.tag === true: // everything
45419                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45420                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45421                     if (this.replaceTag && false === this.replaceTag(e)) {
45422                         return;
45423                     }
45424                     if (e.hasChildNodes()) {
45425                         this.walk(e);
45426                     }
45427                     return;
45428                 
45429                 default:    // tags .. that do not match.
45430                     if (e.hasChildNodes()) {
45431                         this.walk(e);
45432                     }
45433             }
45434             
45435         }, this);
45436         
45437     }
45438 }; 
45439
45440 /**
45441  * @class Roo.htmleditor.FilterAttributes
45442  * clean attributes and  styles including http:// etc.. in attribute
45443  * @constructor
45444 * Run a new Attribute Filter
45445 * @param {Object} config Configuration options
45446  */
45447 Roo.htmleditor.FilterAttributes = function(cfg)
45448 {
45449     Roo.apply(this, cfg);
45450     this.attrib_black = this.attrib_black || [];
45451     this.attrib_white = this.attrib_white || [];
45452
45453     this.attrib_clean = this.attrib_clean || [];
45454     this.style_white = this.style_white || [];
45455     this.style_black = this.style_black || [];
45456     this.walk(cfg.node);
45457 }
45458
45459 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45460 {
45461     tag: true, // all tags
45462     
45463     attrib_black : false, // array
45464     attrib_clean : false,
45465     attrib_white : false,
45466
45467     style_white : false,
45468     style_black : false,
45469      
45470      
45471     replaceTag : function(node)
45472     {
45473         if (!node.attributes || !node.attributes.length) {
45474             return true;
45475         }
45476         
45477         for (var i = node.attributes.length-1; i > -1 ; i--) {
45478             var a = node.attributes[i];
45479             //console.log(a);
45480             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45481                 node.removeAttribute(a.name);
45482                 continue;
45483             }
45484             
45485             
45486             
45487             if (a.name.toLowerCase().substr(0,2)=='on')  {
45488                 node.removeAttribute(a.name);
45489                 continue;
45490             }
45491             
45492             
45493             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45494                 node.removeAttribute(a.name);
45495                 continue;
45496             }
45497             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45498                 this.cleanAttr(node,a.name,a.value); // fixme..
45499                 continue;
45500             }
45501             if (a.name == 'style') {
45502                 this.cleanStyle(node,a.name,a.value);
45503                 continue;
45504             }
45505             /// clean up MS crap..
45506             // tecnically this should be a list of valid class'es..
45507             
45508             
45509             if (a.name == 'class') {
45510                 if (a.value.match(/^Mso/)) {
45511                     node.removeAttribute('class');
45512                 }
45513                 
45514                 if (a.value.match(/^body$/)) {
45515                     node.removeAttribute('class');
45516                 }
45517                 continue;
45518             }
45519             
45520             
45521             // style cleanup!?
45522             // class cleanup?
45523             
45524         }
45525         return true; // clean children
45526     },
45527         
45528     cleanAttr: function(node, n,v)
45529     {
45530         
45531         if (v.match(/^\./) || v.match(/^\//)) {
45532             return;
45533         }
45534         if (v.match(/^(http|https):\/\//)
45535             || v.match(/^mailto:/) 
45536             || v.match(/^ftp:/)
45537             || v.match(/^data:/)
45538             ) {
45539             return;
45540         }
45541         if (v.match(/^#/)) {
45542             return;
45543         }
45544         if (v.match(/^\{/)) { // allow template editing.
45545             return;
45546         }
45547 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45548         node.removeAttribute(n);
45549         
45550     },
45551     cleanStyle : function(node,  n,v)
45552     {
45553         if (v.match(/expression/)) { //XSS?? should we even bother..
45554             node.removeAttribute(n);
45555             return;
45556         }
45557         
45558         var parts = v.split(/;/);
45559         var clean = [];
45560         
45561         Roo.each(parts, function(p) {
45562             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45563             if (!p.length) {
45564                 return true;
45565             }
45566             var l = p.split(':').shift().replace(/\s+/g,'');
45567             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45568             
45569             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45570                 return true;
45571             }
45572             //Roo.log()
45573             // only allow 'c whitelisted system attributes'
45574             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45575                 return true;
45576             }
45577             
45578             
45579             clean.push(p);
45580             return true;
45581         },this);
45582         if (clean.length) { 
45583             node.setAttribute(n, clean.join(';'));
45584         } else {
45585             node.removeAttribute(n);
45586         }
45587         
45588     }
45589         
45590         
45591         
45592     
45593 });/**
45594  * @class Roo.htmleditor.FilterBlack
45595  * remove blacklisted elements.
45596  * @constructor
45597  * Run a new Blacklisted Filter
45598  * @param {Object} config Configuration options
45599  */
45600
45601 Roo.htmleditor.FilterBlack = function(cfg)
45602 {
45603     Roo.apply(this, cfg);
45604     this.walk(cfg.node);
45605 }
45606
45607 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45608 {
45609     tag : true, // all elements.
45610    
45611     replace : function(n)
45612     {
45613         n.parentNode.removeChild(n);
45614     }
45615 });
45616 /**
45617  * @class Roo.htmleditor.FilterComment
45618  * remove comments.
45619  * @constructor
45620 * Run a new Comments Filter
45621 * @param {Object} config Configuration options
45622  */
45623 Roo.htmleditor.FilterComment = function(cfg)
45624 {
45625     this.walk(cfg.node);
45626 }
45627
45628 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45629 {
45630   
45631     replaceComment : function(n)
45632     {
45633         n.parentNode.removeChild(n);
45634     }
45635 });/**
45636  * @class Roo.htmleditor.FilterKeepChildren
45637  * remove tags but keep children
45638  * @constructor
45639  * Run a new Keep Children Filter
45640  * @param {Object} config Configuration options
45641  */
45642
45643 Roo.htmleditor.FilterKeepChildren = function(cfg)
45644 {
45645     Roo.apply(this, cfg);
45646     if (this.tag === false) {
45647         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45648     }
45649     this.walk(cfg.node);
45650 }
45651
45652 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45653 {
45654     
45655   
45656     replaceTag : function(node)
45657     {
45658         // walk children...
45659         //Roo.log(node);
45660         var ar = Array.from(node.childNodes);
45661         //remove first..
45662         for (var i = 0; i < ar.length; i++) {
45663             if (ar[i].nodeType == 1) {
45664                 if (
45665                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45666                     || // array and it matches
45667                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45668                 ) {
45669                     this.replaceTag(ar[i]); // child is blacklisted as well...
45670                     continue;
45671                 }
45672             }
45673         }  
45674         ar = Array.from(node.childNodes);
45675         for (var i = 0; i < ar.length; i++) {
45676          
45677             node.removeChild(ar[i]);
45678             // what if we need to walk these???
45679             node.parentNode.insertBefore(ar[i], node);
45680             if (this.tag !== false) {
45681                 this.walk(ar[i]);
45682                 
45683             }
45684         }
45685         node.parentNode.removeChild(node);
45686         return false; // don't walk children
45687         
45688         
45689     }
45690 });/**
45691  * @class Roo.htmleditor.FilterParagraph
45692  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45693  * like on 'push' to remove the <p> tags and replace them with line breaks.
45694  * @constructor
45695  * Run a new Paragraph Filter
45696  * @param {Object} config Configuration options
45697  */
45698
45699 Roo.htmleditor.FilterParagraph = function(cfg)
45700 {
45701     // no need to apply config.
45702     this.walk(cfg.node);
45703 }
45704
45705 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45706 {
45707     
45708      
45709     tag : 'P',
45710     
45711      
45712     replaceTag : function(node)
45713     {
45714         
45715         if (node.childNodes.length == 1 &&
45716             node.childNodes[0].nodeType == 3 &&
45717             node.childNodes[0].textContent.trim().length < 1
45718             ) {
45719             // remove and replace with '<BR>';
45720             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45721             return false; // no need to walk..
45722         }
45723         var ar = Array.from(node.childNodes);
45724         for (var i = 0; i < ar.length; i++) {
45725             node.removeChild(ar[i]);
45726             // what if we need to walk these???
45727             node.parentNode.insertBefore(ar[i], node);
45728         }
45729         // now what about this?
45730         // <p> &nbsp; </p>
45731         
45732         // double BR.
45733         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45734         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45735         node.parentNode.removeChild(node);
45736         
45737         return false;
45738
45739     }
45740     
45741 });/**
45742  * @class Roo.htmleditor.FilterSpan
45743  * filter span's with no attributes out..
45744  * @constructor
45745  * Run a new Span Filter
45746  * @param {Object} config Configuration options
45747  */
45748
45749 Roo.htmleditor.FilterSpan = function(cfg)
45750 {
45751     // no need to apply config.
45752     this.walk(cfg.node);
45753 }
45754
45755 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45756 {
45757      
45758     tag : 'SPAN',
45759      
45760  
45761     replaceTag : function(node)
45762     {
45763         if (node.attributes && node.attributes.length > 0) {
45764             return true; // walk if there are any.
45765         }
45766         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45767         return false;
45768      
45769     }
45770     
45771 });/**
45772  * @class Roo.htmleditor.FilterTableWidth
45773   try and remove table width data - as that frequently messes up other stuff.
45774  * 
45775  *      was cleanTableWidths.
45776  *
45777  * Quite often pasting from word etc.. results in tables with column and widths.
45778  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45779  *
45780  * @constructor
45781  * Run a new Table Filter
45782  * @param {Object} config Configuration options
45783  */
45784
45785 Roo.htmleditor.FilterTableWidth = function(cfg)
45786 {
45787     // no need to apply config.
45788     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45789     this.walk(cfg.node);
45790 }
45791
45792 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45793 {
45794      
45795      
45796     
45797     replaceTag: function(node) {
45798         
45799         
45800       
45801         if (node.hasAttribute('width')) {
45802             node.removeAttribute('width');
45803         }
45804         
45805          
45806         if (node.hasAttribute("style")) {
45807             // pretty basic...
45808             
45809             var styles = node.getAttribute("style").split(";");
45810             var nstyle = [];
45811             Roo.each(styles, function(s) {
45812                 if (!s.match(/:/)) {
45813                     return;
45814                 }
45815                 var kv = s.split(":");
45816                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45817                     return;
45818                 }
45819                 // what ever is left... we allow.
45820                 nstyle.push(s);
45821             });
45822             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45823             if (!nstyle.length) {
45824                 node.removeAttribute('style');
45825             }
45826         }
45827         
45828         return true; // continue doing children..
45829     }
45830 });/**
45831  * @class Roo.htmleditor.FilterWord
45832  * try and clean up all the mess that Word generates.
45833  * 
45834  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45835  
45836  * @constructor
45837  * Run a new Span Filter
45838  * @param {Object} config Configuration options
45839  */
45840
45841 Roo.htmleditor.FilterWord = function(cfg)
45842 {
45843     // no need to apply config.
45844     this.walk(cfg.node);
45845 }
45846
45847 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45848 {
45849     tag: true,
45850      
45851     
45852     /**
45853      * Clean up MS wordisms...
45854      */
45855     replaceTag : function(node)
45856     {
45857          
45858         // no idea what this does - span with text, replaceds with just text.
45859         if(
45860                 node.nodeName == 'SPAN' &&
45861                 !node.hasAttributes() &&
45862                 node.childNodes.length == 1 &&
45863                 node.firstChild.nodeName == "#text"  
45864         ) {
45865             var textNode = node.firstChild;
45866             node.removeChild(textNode);
45867             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45868                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45869             }
45870             node.parentNode.insertBefore(textNode, node);
45871             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45872                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45873             }
45874             
45875             node.parentNode.removeChild(node);
45876             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45877         }
45878         
45879    
45880         
45881         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45882             node.parentNode.removeChild(node);
45883             return false; // dont do chidlren
45884         }
45885         //Roo.log(node.tagName);
45886         // remove - but keep children..
45887         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45888             //Roo.log('-- removed');
45889             while (node.childNodes.length) {
45890                 var cn = node.childNodes[0];
45891                 node.removeChild(cn);
45892                 node.parentNode.insertBefore(cn, node);
45893                 // move node to parent - and clean it..
45894                 this.replaceTag(cn);
45895             }
45896             node.parentNode.removeChild(node);
45897             /// no need to iterate chidlren = it's got none..
45898             //this.iterateChildren(node, this.cleanWord);
45899             return false; // no need to iterate children.
45900         }
45901         // clean styles
45902         if (node.className.length) {
45903             
45904             var cn = node.className.split(/\W+/);
45905             var cna = [];
45906             Roo.each(cn, function(cls) {
45907                 if (cls.match(/Mso[a-zA-Z]+/)) {
45908                     return;
45909                 }
45910                 cna.push(cls);
45911             });
45912             node.className = cna.length ? cna.join(' ') : '';
45913             if (!cna.length) {
45914                 node.removeAttribute("class");
45915             }
45916         }
45917         
45918         if (node.hasAttribute("lang")) {
45919             node.removeAttribute("lang");
45920         }
45921         
45922         if (node.hasAttribute("style")) {
45923             
45924             var styles = node.getAttribute("style").split(";");
45925             var nstyle = [];
45926             Roo.each(styles, function(s) {
45927                 if (!s.match(/:/)) {
45928                     return;
45929                 }
45930                 var kv = s.split(":");
45931                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45932                     return;
45933                 }
45934                 // what ever is left... we allow.
45935                 nstyle.push(s);
45936             });
45937             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45938             if (!nstyle.length) {
45939                 node.removeAttribute('style');
45940             }
45941         }
45942         return true; // do children
45943         
45944         
45945         
45946     }
45947 });
45948 /**
45949  * @class Roo.htmleditor.FilterStyleToTag
45950  * part of the word stuff... - certain 'styles' should be converted to tags.
45951  * eg.
45952  *   font-weight: bold -> bold
45953  *   ?? super / subscrit etc..
45954  * 
45955  * @constructor
45956 * Run a new style to tag filter.
45957 * @param {Object} config Configuration options
45958  */
45959 Roo.htmleditor.FilterStyleToTag = function(cfg)
45960 {
45961     
45962     this.tags = {
45963         B  : [ 'fontWeight' , 'bold'],
45964         I :  [ 'fontStyle' , 'italic'],
45965         //pre :  [ 'font-style' , 'italic'],
45966         // h1.. h6 ?? font-size?
45967         SUP : [ 'verticalAlign' , 'super' ],
45968         SUB : [ 'verticalAlign' , 'sub' ]
45969         
45970         
45971     };
45972     
45973     Roo.apply(this, cfg);
45974      
45975     
45976     this.walk(cfg.node);
45977     
45978     
45979     
45980 }
45981
45982
45983 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45984 {
45985     tag: true, // all tags
45986     
45987     tags : false,
45988     
45989     
45990     replaceTag : function(node)
45991     {
45992         
45993         
45994         if (node.getAttribute("style") === null) {
45995             return true;
45996         }
45997         var inject = [];
45998         for (var k in this.tags) {
45999             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
46000                 inject.push(k);
46001                 node.style.removeProperty(this.tags[k][0]);
46002             }
46003         }
46004         if (!inject.length) {
46005             return true; 
46006         }
46007         var cn = Array.from(node.childNodes);
46008         var nn = node;
46009         Roo.each(inject, function(t) {
46010             var nc = node.ownerDocument.createelement(t);
46011             nn.appendChild(nc);
46012             nn = nc;
46013         });
46014         for(var i = 0;i < cn.length;cn++) {
46015             node.removeChild(cn[i]);
46016             nn.appendChild(cn[i]);
46017         }
46018         return true /// iterate thru
46019     }
46020     
46021 })/**
46022  * @class Roo.htmleditor.FilterLongBr
46023  * BR/BR/BR - keep a maximum of 2...
46024  * @constructor
46025  * Run a new Long BR Filter
46026  * @param {Object} config Configuration options
46027  */
46028
46029 Roo.htmleditor.FilterLongBr = function(cfg)
46030 {
46031     // no need to apply config.
46032     this.walk(cfg.node);
46033 }
46034
46035 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
46036 {
46037     
46038      
46039     tag : 'BR',
46040     
46041      
46042     replaceTag : function(node)
46043     {
46044         
46045         var ps = node.nextSibling;
46046         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46047             ps = ps.nextSibling;
46048         }
46049         
46050         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
46051             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
46052             return false;
46053         }
46054         
46055         if (!ps || ps.nodeType != 1) {
46056             return false;
46057         }
46058         
46059         if (!ps || ps.tagName != 'BR') {
46060            
46061             return false;
46062         }
46063         
46064         
46065         
46066         
46067         
46068         if (!node.previousSibling) {
46069             return false;
46070         }
46071         var ps = node.previousSibling;
46072         
46073         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
46074             ps = ps.previousSibling;
46075         }
46076         if (!ps || ps.nodeType != 1) {
46077             return false;
46078         }
46079         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
46080         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
46081             return false;
46082         }
46083         
46084         node.parentNode.removeChild(node); // remove me...
46085         
46086         return false; // no need to do children
46087
46088     }
46089     
46090 });
46091 /**
46092  * @class Roo.htmleditor.Tidy
46093  * Tidy HTML 
46094  * @cfg {Roo.HtmlEditorCore} core the editor.
46095  * @constructor
46096  * Create a new Filter.
46097  * @param {Object} config Configuration options
46098  */
46099
46100
46101 Roo.htmleditor.Tidy = function(cfg) {
46102     Roo.apply(this, cfg);
46103     
46104     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
46105      
46106 }
46107
46108 Roo.htmleditor.Tidy.toString = function(node)
46109 {
46110     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
46111 }
46112
46113 Roo.htmleditor.Tidy.prototype = {
46114     
46115     
46116     wrap : function(s) {
46117         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
46118     },
46119
46120     
46121     tidy : function(node, indent) {
46122      
46123         if  (node.nodeType == 3) {
46124             // text.
46125             
46126             
46127             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
46128                 
46129             
46130         }
46131         
46132         if  (node.nodeType != 1) {
46133             return '';
46134         }
46135         
46136         
46137         
46138         if (node.tagName == 'BODY') {
46139             
46140             return this.cn(node, '');
46141         }
46142              
46143              // Prints the node tagName, such as <A>, <IMG>, etc
46144         var ret = "<" + node.tagName +  this.attr(node) ;
46145         
46146         // elements with no children..
46147         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
46148                 return ret + '/>';
46149         }
46150         ret += '>';
46151         
46152         
46153         var cindent = indent === false ? '' : (indent + '  ');
46154         // tags where we will not pad the children.. (inline text tags etc..)
46155         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
46156             cindent = false;
46157             
46158             
46159         }
46160         
46161         var cn = this.cn(node, cindent );
46162         
46163         return ret + cn  + '</' + node.tagName + '>';
46164         
46165     },
46166     cn: function(node, indent)
46167     {
46168         var ret = [];
46169         
46170         var ar = Array.from(node.childNodes);
46171         for (var i = 0 ; i < ar.length ; i++) {
46172             
46173             
46174             
46175             if (indent !== false   // indent==false preservies everything
46176                 && i > 0
46177                 && ar[i].nodeType == 3 
46178                 && ar[i].nodeValue.length > 0
46179                 && ar[i].nodeValue.match(/^\s+/)
46180             ) {
46181                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
46182                     ret.pop(); // remove line break from last?
46183                 }
46184                 
46185                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
46186             }
46187             if (indent !== false
46188                 && ar[i].nodeType == 1 // element - and indent is not set... 
46189             ) {
46190                 ret.push("\n" + indent); 
46191             }
46192             
46193             ret.push(this.tidy(ar[i], indent));
46194             // text + trailing indent 
46195             if (indent !== false
46196                 && ar[i].nodeType == 3
46197                 && ar[i].nodeValue.length > 0
46198                 && ar[i].nodeValue.match(/\s+$/)
46199             ){
46200                 ret.push("\n" + indent); 
46201             }
46202             
46203             
46204             
46205             
46206         }
46207         // what if all text?
46208         
46209         
46210         return ret.join('');
46211     },
46212     
46213          
46214         
46215     attr : function(node)
46216     {
46217         var attr = [];
46218         for(i = 0; i < node.attributes.length;i++) {
46219             
46220             // skip empty values?
46221             if (!node.attributes.item(i).value.length) {
46222                 continue;
46223             }
46224             attr.push(  node.attributes.item(i).name + '="' +
46225                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
46226             );
46227         }
46228         return attr.length ? (' ' + attr.join(' ') ) : '';
46229         
46230     }
46231     
46232     
46233     
46234 }
46235 /**
46236  * @class Roo.htmleditor.KeyEnter
46237  * Handle Enter press..
46238  * @cfg {Roo.HtmlEditorCore} core the editor.
46239  * @constructor
46240  * Create a new Filter.
46241  * @param {Object} config Configuration options
46242  */
46243
46244
46245
46246 Roo.htmleditor.KeyEnter = function(cfg) {
46247     Roo.apply(this, cfg);
46248     // this does not actually call walk as it's really just a abstract class
46249  
46250     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
46251 }
46252
46253
46254 Roo.htmleditor.KeyEnter.prototype = {
46255     
46256     core : false,
46257     
46258     keypress : function(e)
46259     {
46260         if (e.charCode != 13) {
46261             return true;
46262         }
46263         e.preventDefault();
46264         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
46265         var doc = this.core.doc;
46266         
46267         var docFragment = doc.createDocumentFragment();
46268     
46269         //add a new line
46270         var newEle = doc.createTextNode('\n');
46271         docFragment.appendChild(newEle);
46272     
46273     
46274         var range = this.core.win.getSelection().getRangeAt(0);
46275         var n = range.commonAncestorContainer ;
46276         while (n && n.nodeType != 1) {
46277             n  = n.parentNode;
46278         }
46279         var li = false;
46280         if (n && n.tagName == 'UL') {
46281             li = doc.createElement('LI');
46282             n.appendChild(li);
46283             
46284         }
46285         if (n && n.tagName == 'LI') {
46286             li = doc.createElement('LI');
46287             if (n.nextSibling) {
46288                 n.parentNode.insertBefore(li, n.firstSibling);
46289                 
46290             } else {
46291                 n.parentNode.appendChild(li);
46292             }
46293         }
46294         if (li) {   
46295             range = doc.createRange();
46296             range.setStartAfter(li);
46297             range.collapse(true);
46298         
46299             //make the cursor there
46300             var sel = this.core.win.getSelection();
46301             sel.removeAllRanges();
46302             sel.addRange(range);
46303             return false;
46304             
46305             
46306         }
46307         //add the br, or p, or something else
46308         newEle = doc.createElement('br');
46309         docFragment.appendChild(newEle);
46310     
46311         //make the br replace selection
46312         
46313         range.deleteContents();
46314         
46315         range.insertNode(docFragment);
46316         range = range.cloneRange();
46317         range.collapse(false);
46318          
46319         win.getSelection().removeAllRanges();
46320         win.getSelection().addRange(range);
46321         
46322     
46323         
46324     
46325         return false;
46326          
46327     }
46328 };
46329      
46330 /**
46331  * @class Roo.htmleditor.Block
46332  * Base class for html editor blocks - do not use it directly .. extend it..
46333  * @cfg {DomElement} node The node to apply stuff to.
46334  * @cfg {String} friendly_name the name that appears in the context bar about this block
46335  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46336  
46337  * @constructor
46338  * Create a new Filter.
46339  * @param {Object} config Configuration options
46340  */
46341
46342 Roo.htmleditor.Block  = function(cfg)
46343 {
46344     // do nothing .. should not be called really.
46345 }
46346
46347 Roo.htmleditor.Block.factory = function(node)
46348 {
46349     
46350     var id = Roo.get(node).id;
46351     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46352         Roo.htmleditor.Block.cache[id].readElement();
46353         return Roo.htmleditor.Block.cache[id];
46354     }
46355     
46356     var cls = Roo.htmleditor['Block' + node.getAttribute('data-block')];
46357     if (typeof(cls) == 'undefined') {
46358         Roo.log("OOps missing block : " + 'Block' + node.getAttribute('data-block'));
46359         return false;
46360     }
46361     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46362     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46363 };
46364 // question goes here... do we need to clear out this cache sometimes?
46365 // or show we make it relivant to the htmleditor.
46366 Roo.htmleditor.Block.cache = {};
46367
46368 Roo.htmleditor.Block.prototype = {
46369     
46370     node : false,
46371     
46372      // used by context menu
46373     friendly_name : 'Image with caption',
46374     
46375     context : false,
46376     /**
46377      * Update a node with values from this object
46378      * @param {DomElement} node
46379      */
46380     updateElement : function(node)
46381     {
46382         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46383     },
46384      /**
46385      * convert to plain HTML for calling insertAtCursor..
46386      */
46387     toHTML : function()
46388     {
46389         return Roo.DomHelper.markup(this.toObject());
46390     },
46391     /**
46392      * used by readEleemnt to extract data from a node
46393      * may need improving as it's pretty basic
46394      
46395      * @param {DomElement} node
46396      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46397      * @param {String} attribute (use html - for contents, or style for using next param as style)
46398      * @param {String} style the style property - eg. text-align
46399      */
46400     getVal : function(node, tag, attr, style)
46401     {
46402         var n = node;
46403         if (tag !== true && n.tagName != tag.toUpperCase()) {
46404             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46405             // but kiss for now.
46406             n = node.getElementsByTagName(tag).item(0);
46407         }
46408         if (attr == 'html') {
46409             return n.innerHTML;
46410         }
46411         if (attr == 'style') {
46412             return n.style[style]
46413         }
46414         
46415         return Roo.get(n).attr(attr);
46416             
46417     },
46418     /**
46419      * create a DomHelper friendly object - for use with 
46420      * Roo.DomHelper.markup / overwrite / etc..
46421      * (override this)
46422      */
46423     toObject : function()
46424     {
46425         return {};
46426     },
46427       /**
46428      * Read a node that has a 'data-block' property - and extract the values from it.
46429      * @param {DomElement} node - the node
46430      */
46431     readElement : function(node)
46432     {
46433         
46434     } 
46435     
46436     
46437 };
46438
46439  
46440
46441 /**
46442  * @class Roo.htmleditor.BlockFigure
46443  * Block that has an image and a figcaption
46444  * @cfg {String} image_src the url for the image
46445  * @cfg {String} align (left|right) alignment for the block default left
46446  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46447  * @cfg {String} caption the text to appear below  (and in the alt tag)
46448  * @cfg {String|number} image_width the width of the image number or %?
46449  * @cfg {String|number} image_height the height of the image number or %?
46450  * 
46451  * @constructor
46452  * Create a new Filter.
46453  * @param {Object} config Configuration options
46454  */
46455
46456 Roo.htmleditor.BlockFigure = function(cfg)
46457 {
46458     if (cfg.node) {
46459         this.readElement(cfg.node);
46460         this.updateElement(cfg.node);
46461     }
46462     Roo.apply(this, cfg);
46463 }
46464 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46465  
46466     
46467     // setable values.
46468     image_src: '',
46469     
46470     align: 'left',
46471     caption : '',
46472     text_align: 'left',
46473     
46474     width : '46%',
46475     margin: '2%',
46476     
46477     // used by context menu
46478     friendly_name : 'Image with caption',
46479     
46480     context : { // ?? static really
46481         width : {
46482             title: "Width",
46483             width: 40
46484             // ?? number
46485         },
46486         margin : {
46487             title: "Margin",
46488             width: 40
46489             // ?? number
46490         },
46491         align: {
46492             title: "Align",
46493             opts : [[ "left"],[ "right"]],
46494             width : 80
46495             
46496         },
46497         text_align: {
46498             title: "Caption Align",
46499             opts : [ [ "left"],[ "right"],[ "center"]],
46500             width : 80
46501         },
46502         
46503        
46504         image_src : {
46505             title: "Src",
46506             width: 220
46507         }
46508     },
46509     /**
46510      * create a DomHelper friendly object - for use with
46511      * Roo.DomHelper.markup / overwrite / etc..
46512      */
46513     toObject : function()
46514     {
46515         var d = document.createElement('div');
46516         d.innerHTML = this.caption;
46517         
46518         return {
46519             tag: 'figure',
46520             'data-block' : 'Figure',
46521             contenteditable : 'false',
46522             style : {
46523                 display: 'table',
46524                 float :  this.align ,
46525                 width :  this.width,
46526                 margin:  this.margin
46527             },
46528             cn : [
46529                 {
46530                     tag : 'img',
46531                     src : this.image_src,
46532                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46533                     style: {
46534                         width: '100%'
46535                     }
46536                 },
46537                 {
46538                     tag: 'figcaption',
46539                     contenteditable : true,
46540                     style : {
46541                         'text-align': this.text_align
46542                     },
46543                     html : this.caption
46544                     
46545                 }
46546             ]
46547         };
46548     },
46549     
46550     readElement : function(node)
46551     {
46552         this.image_src = this.getVal(node, 'img', 'src');
46553         this.align = this.getVal(node, 'figure', 'style', 'float');
46554         this.caption = this.getVal(node, 'figcaption', 'html');
46555         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46556         this.width = this.getVal(node, 'figure', 'style', 'width');
46557         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46558         
46559     } 
46560     
46561   
46562    
46563      
46564     
46565     
46566     
46567     
46568 })
46569
46570 //<script type="text/javascript">
46571
46572 /*
46573  * Based  Ext JS Library 1.1.1
46574  * Copyright(c) 2006-2007, Ext JS, LLC.
46575  * LGPL
46576  *
46577  */
46578  
46579 /**
46580  * @class Roo.HtmlEditorCore
46581  * @extends Roo.Component
46582  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46583  *
46584  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46585  */
46586
46587 Roo.HtmlEditorCore = function(config){
46588     
46589     
46590     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46591     
46592     
46593     this.addEvents({
46594         /**
46595          * @event initialize
46596          * Fires when the editor is fully initialized (including the iframe)
46597          * @param {Roo.HtmlEditorCore} this
46598          */
46599         initialize: true,
46600         /**
46601          * @event activate
46602          * Fires when the editor is first receives the focus. Any insertion must wait
46603          * until after this event.
46604          * @param {Roo.HtmlEditorCore} this
46605          */
46606         activate: true,
46607          /**
46608          * @event beforesync
46609          * Fires before the textarea is updated with content from the editor iframe. Return false
46610          * to cancel the sync.
46611          * @param {Roo.HtmlEditorCore} this
46612          * @param {String} html
46613          */
46614         beforesync: true,
46615          /**
46616          * @event beforepush
46617          * Fires before the iframe editor is updated with content from the textarea. Return false
46618          * to cancel the push.
46619          * @param {Roo.HtmlEditorCore} this
46620          * @param {String} html
46621          */
46622         beforepush: true,
46623          /**
46624          * @event sync
46625          * Fires when the textarea is updated with content from the editor iframe.
46626          * @param {Roo.HtmlEditorCore} this
46627          * @param {String} html
46628          */
46629         sync: true,
46630          /**
46631          * @event push
46632          * Fires when the iframe editor is updated with content from the textarea.
46633          * @param {Roo.HtmlEditorCore} this
46634          * @param {String} html
46635          */
46636         push: true,
46637         
46638         /**
46639          * @event editorevent
46640          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46641          * @param {Roo.HtmlEditorCore} this
46642          */
46643         editorevent: true
46644         
46645     });
46646     
46647     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46648     
46649     // defaults : white / black...
46650     this.applyBlacklists();
46651     
46652     
46653     
46654 };
46655
46656
46657 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46658
46659
46660      /**
46661      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46662      */
46663     
46664     owner : false,
46665     
46666      /**
46667      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46668      *                        Roo.resizable.
46669      */
46670     resizable : false,
46671      /**
46672      * @cfg {Number} height (in pixels)
46673      */   
46674     height: 300,
46675    /**
46676      * @cfg {Number} width (in pixels)
46677      */   
46678     width: 500,
46679     
46680     /**
46681      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46682      * 
46683      */
46684     stylesheets: false,
46685     
46686     /**
46687      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46688      */
46689     allowComments: false,
46690     // id of frame..
46691     frameId: false,
46692     
46693     // private properties
46694     validationEvent : false,
46695     deferHeight: true,
46696     initialized : false,
46697     activated : false,
46698     sourceEditMode : false,
46699     onFocus : Roo.emptyFn,
46700     iframePad:3,
46701     hideMode:'offsets',
46702     
46703     clearUp: true,
46704     
46705     // blacklist + whitelisted elements..
46706     black: false,
46707     white: false,
46708      
46709     bodyCls : '',
46710
46711     
46712     undoManager : false,
46713     /**
46714      * Protected method that will not generally be called directly. It
46715      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46716      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46717      */
46718     getDocMarkup : function(){
46719         // body styles..
46720         var st = '';
46721         
46722         // inherit styels from page...?? 
46723         if (this.stylesheets === false) {
46724             
46725             Roo.get(document.head).select('style').each(function(node) {
46726                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46727             });
46728             
46729             Roo.get(document.head).select('link').each(function(node) { 
46730                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46731             });
46732             
46733         } else if (!this.stylesheets.length) {
46734                 // simple..
46735                 st = '<style type="text/css">' +
46736                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46737                    '</style>';
46738         } else {
46739             for (var i in this.stylesheets) {
46740                 if (typeof(this.stylesheets[i]) != 'string') {
46741                     continue;
46742                 }
46743                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46744             }
46745             
46746         }
46747         
46748         st +=  '<style type="text/css">' +
46749             'IMG { cursor: pointer } ' +
46750         '</style>';
46751
46752         var cls = 'roo-htmleditor-body';
46753         
46754         if(this.bodyCls.length){
46755             cls += ' ' + this.bodyCls;
46756         }
46757         
46758         return '<html><head>' + st  +
46759             //<style type="text/css">' +
46760             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46761             //'</style>' +
46762             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46763     },
46764
46765     // private
46766     onRender : function(ct, position)
46767     {
46768         var _t = this;
46769         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46770         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46771         
46772         
46773         this.el.dom.style.border = '0 none';
46774         this.el.dom.setAttribute('tabIndex', -1);
46775         this.el.addClass('x-hidden hide');
46776         
46777         
46778         
46779         if(Roo.isIE){ // fix IE 1px bogus margin
46780             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46781         }
46782        
46783         
46784         this.frameId = Roo.id();
46785         
46786          
46787         
46788         var iframe = this.owner.wrap.createChild({
46789             tag: 'iframe',
46790             cls: 'form-control', // bootstrap..
46791             id: this.frameId,
46792             name: this.frameId,
46793             frameBorder : 'no',
46794             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46795         }, this.el
46796         );
46797         
46798         
46799         this.iframe = iframe.dom;
46800
46801         this.assignDocWin();
46802         
46803         this.doc.designMode = 'on';
46804        
46805         this.doc.open();
46806         this.doc.write(this.getDocMarkup());
46807         this.doc.close();
46808
46809         
46810         var task = { // must defer to wait for browser to be ready
46811             run : function(){
46812                 //console.log("run task?" + this.doc.readyState);
46813                 this.assignDocWin();
46814                 if(this.doc.body || this.doc.readyState == 'complete'){
46815                     try {
46816                         this.doc.designMode="on";
46817                         
46818                     } catch (e) {
46819                         return;
46820                     }
46821                     Roo.TaskMgr.stop(task);
46822                     this.initEditor.defer(10, this);
46823                 }
46824             },
46825             interval : 10,
46826             duration: 10000,
46827             scope: this
46828         };
46829         Roo.TaskMgr.start(task);
46830
46831     },
46832
46833     // private
46834     onResize : function(w, h)
46835     {
46836          Roo.log('resize: ' +w + ',' + h );
46837         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46838         if(!this.iframe){
46839             return;
46840         }
46841         if(typeof w == 'number'){
46842             
46843             this.iframe.style.width = w + 'px';
46844         }
46845         if(typeof h == 'number'){
46846             
46847             this.iframe.style.height = h + 'px';
46848             if(this.doc){
46849                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46850             }
46851         }
46852         
46853     },
46854
46855     /**
46856      * Toggles the editor between standard and source edit mode.
46857      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46858      */
46859     toggleSourceEdit : function(sourceEditMode){
46860         
46861         this.sourceEditMode = sourceEditMode === true;
46862         
46863         if(this.sourceEditMode){
46864  
46865             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46866             
46867         }else{
46868             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46869             //this.iframe.className = '';
46870             this.deferFocus();
46871         }
46872         //this.setSize(this.owner.wrap.getSize());
46873         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46874     },
46875
46876     
46877   
46878
46879     /**
46880      * Protected method that will not generally be called directly. If you need/want
46881      * custom HTML cleanup, this is the method you should override.
46882      * @param {String} html The HTML to be cleaned
46883      * return {String} The cleaned HTML
46884      */
46885     cleanHtml : function(html){
46886         html = String(html);
46887         if(html.length > 5){
46888             if(Roo.isSafari){ // strip safari nonsense
46889                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46890             }
46891         }
46892         if(html == '&nbsp;'){
46893             html = '';
46894         }
46895         return html;
46896     },
46897
46898     /**
46899      * HTML Editor -> Textarea
46900      * Protected method that will not generally be called directly. Syncs the contents
46901      * of the editor iframe with the textarea.
46902      */
46903     syncValue : function()
46904     {
46905         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46906         if(this.initialized){
46907             
46908             this.undoManager.addEvent();
46909
46910             
46911             var bd = (this.doc.body || this.doc.documentElement);
46912             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46913             
46914             // not sure if this is really the place for this
46915             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46916             // this has to update attributes that get duped.. like alt and caption..
46917             
46918             
46919             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46920             //     Roo.htmleditor.Block.factory(e);
46921             //},this);
46922             
46923             
46924             var div = document.createElement('div');
46925             div.innerHTML = bd.innerHTML;
46926             // remove content editable. (blocks)
46927             
46928            
46929             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46930             //?? tidy?
46931             var html = div.innerHTML;
46932             if(Roo.isSafari){
46933                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46934                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46935                 if(m && m[1]){
46936                     html = '<div style="'+m[0]+'">' + html + '</div>';
46937                 }
46938             }
46939             html = this.cleanHtml(html);
46940             // fix up the special chars.. normaly like back quotes in word...
46941             // however we do not want to do this with chinese..
46942             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46943                 
46944                 var cc = match.charCodeAt();
46945
46946                 // Get the character value, handling surrogate pairs
46947                 if (match.length == 2) {
46948                     // It's a surrogate pair, calculate the Unicode code point
46949                     var high = match.charCodeAt(0) - 0xD800;
46950                     var low  = match.charCodeAt(1) - 0xDC00;
46951                     cc = (high * 0x400) + low + 0x10000;
46952                 }  else if (
46953                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46954                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46955                     (cc >= 0xf900 && cc < 0xfb00 )
46956                 ) {
46957                         return match;
46958                 }  
46959          
46960                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46961                 return "&#" + cc + ";";
46962                 
46963                 
46964             });
46965             
46966             
46967              
46968             if(this.owner.fireEvent('beforesync', this, html) !== false){
46969                 this.el.dom.value = html;
46970                 this.owner.fireEvent('sync', this, html);
46971             }
46972         }
46973     },
46974
46975     /**
46976      * TEXTAREA -> EDITABLE
46977      * Protected method that will not generally be called directly. Pushes the value of the textarea
46978      * into the iframe editor.
46979      */
46980     pushValue : function()
46981     {
46982         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46983         if(this.initialized){
46984             var v = this.el.dom.value.trim();
46985             
46986             
46987             if(this.owner.fireEvent('beforepush', this, v) !== false){
46988                 var d = (this.doc.body || this.doc.documentElement);
46989                 d.innerHTML = v;
46990                  
46991                 this.el.dom.value = d.innerHTML;
46992                 this.owner.fireEvent('push', this, v);
46993             }
46994             
46995             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46996                 
46997                 Roo.htmleditor.Block.factory(e);
46998                 
46999             },this);
47000             var lc = this.doc.body.lastChild;
47001             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
47002                 // add an extra line at the end.
47003                 this.doc.body.appendChild(this.doc.createElement('br'));
47004             }
47005             
47006             
47007         }
47008     },
47009
47010     // private
47011     deferFocus : function(){
47012         this.focus.defer(10, this);
47013     },
47014
47015     // doc'ed in Field
47016     focus : function(){
47017         if(this.win && !this.sourceEditMode){
47018             this.win.focus();
47019         }else{
47020             this.el.focus();
47021         }
47022     },
47023     
47024     assignDocWin: function()
47025     {
47026         var iframe = this.iframe;
47027         
47028          if(Roo.isIE){
47029             this.doc = iframe.contentWindow.document;
47030             this.win = iframe.contentWindow;
47031         } else {
47032 //            if (!Roo.get(this.frameId)) {
47033 //                return;
47034 //            }
47035 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47036 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47037             
47038             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47039                 return;
47040             }
47041             
47042             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47043             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47044         }
47045     },
47046     
47047     // private
47048     initEditor : function(){
47049         //console.log("INIT EDITOR");
47050         this.assignDocWin();
47051         
47052         
47053         
47054         this.doc.designMode="on";
47055         this.doc.open();
47056         this.doc.write(this.getDocMarkup());
47057         this.doc.close();
47058         
47059         var dbody = (this.doc.body || this.doc.documentElement);
47060         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47061         // this copies styles from the containing element into thsi one..
47062         // not sure why we need all of this..
47063         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47064         
47065         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47066         //ss['background-attachment'] = 'fixed'; // w3c
47067         dbody.bgProperties = 'fixed'; // ie
47068         //Roo.DomHelper.applyStyles(dbody, ss);
47069         Roo.EventManager.on(this.doc, {
47070             //'mousedown': this.onEditorEvent,
47071             'mouseup': this.onEditorEvent,
47072             'dblclick': this.onEditorEvent,
47073             'click': this.onEditorEvent,
47074             'keyup': this.onEditorEvent,
47075             
47076             buffer:100,
47077             scope: this
47078         });
47079         Roo.EventManager.on(this.doc, {
47080             'paste': this.onPasteEvent,
47081             scope : this
47082         });
47083         if(Roo.isGecko){
47084             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47085         }
47086         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47087             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47088         }
47089         this.initialized = true;
47090
47091         
47092         // initialize special key events - enter
47093         new Roo.htmleditor.KeyEnter({core : this});
47094         
47095          
47096         
47097         this.owner.fireEvent('initialize', this);
47098         this.pushValue();
47099     },
47100     
47101     onPasteEvent : function(e,v)
47102     {
47103         // I think we better assume paste is going to be a dirty load of rubish from word..
47104         
47105         // even pasting into a 'email version' of this widget will have to clean up that mess.
47106         var cd = (e.browserEvent.clipboardData || window.clipboardData);
47107         
47108         // check what type of paste - if it's an image, then handle it differently.
47109         if (cd.files.length > 0) {
47110             // pasting images?
47111             var urlAPI = (window.createObjectURL && window) || 
47112                 (window.URL && URL.revokeObjectURL && URL) || 
47113                 (window.webkitURL && webkitURL);
47114     
47115             var url = urlAPI.createObjectURL( cd.files[0]);
47116             this.insertAtCursor('<img src=" + url + ">');
47117             return false;
47118         }
47119         
47120         var html = cd.getData('text/html'); // clipboard event
47121         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47122         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47123         Roo.log(images);
47124         //Roo.log(imgs);
47125         // fixme..
47126         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47127                        .map(function(g) { return g.toDataURL(); });
47128         
47129         
47130         html = this.cleanWordChars(html);
47131         
47132         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47133         
47134         if (images.length > 0) {
47135             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47136                 img.setAttribute('src', images[i]);
47137             });
47138         }
47139         
47140       
47141         new Roo.htmleditor.FilterStyleToTag({ node : d });
47142         new Roo.htmleditor.FilterAttributes({
47143             node : d,
47144             attrib_white : ['href', 'src', 'name', 'align'],
47145             attrib_clean : ['href', 'src' ] 
47146         });
47147         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47148         // should be fonts..
47149         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47150         new Roo.htmleditor.FilterParagraph({ node : d });
47151         new Roo.htmleditor.FilterSpan({ node : d });
47152         new Roo.htmleditor.FilterLongBr({ node : d });
47153         
47154         
47155         
47156         this.insertAtCursor(d.innerHTML);
47157         
47158         e.preventDefault();
47159         return false;
47160         // default behaveiour should be our local cleanup paste? (optional?)
47161         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47162         //this.owner.fireEvent('paste', e, v);
47163     },
47164     // private
47165     onDestroy : function(){
47166         
47167         
47168         
47169         if(this.rendered){
47170             
47171             //for (var i =0; i < this.toolbars.length;i++) {
47172             //    // fixme - ask toolbars for heights?
47173             //    this.toolbars[i].onDestroy();
47174            // }
47175             
47176             //this.wrap.dom.innerHTML = '';
47177             //this.wrap.remove();
47178         }
47179     },
47180
47181     // private
47182     onFirstFocus : function(){
47183         
47184         this.assignDocWin();
47185         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47186         
47187         this.activated = true;
47188          
47189     
47190         if(Roo.isGecko){ // prevent silly gecko errors
47191             this.win.focus();
47192             var s = this.win.getSelection();
47193             if(!s.focusNode || s.focusNode.nodeType != 3){
47194                 var r = s.getRangeAt(0);
47195                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47196                 r.collapse(true);
47197                 this.deferFocus();
47198             }
47199             try{
47200                 this.execCmd('useCSS', true);
47201                 this.execCmd('styleWithCSS', false);
47202             }catch(e){}
47203         }
47204         this.owner.fireEvent('activate', this);
47205     },
47206
47207     // private
47208     adjustFont: function(btn){
47209         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47210         //if(Roo.isSafari){ // safari
47211         //    adjust *= 2;
47212        // }
47213         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47214         if(Roo.isSafari){ // safari
47215             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47216             v =  (v < 10) ? 10 : v;
47217             v =  (v > 48) ? 48 : v;
47218             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47219             
47220         }
47221         
47222         
47223         v = Math.max(1, v+adjust);
47224         
47225         this.execCmd('FontSize', v  );
47226     },
47227
47228     onEditorEvent : function(e)
47229     {
47230         this.owner.fireEvent('editorevent', this, e);
47231       //  this.updateToolbar();
47232         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47233     },
47234
47235     insertTag : function(tg)
47236     {
47237         // could be a bit smarter... -> wrap the current selected tRoo..
47238         if (tg.toLowerCase() == 'span' ||
47239             tg.toLowerCase() == 'code' ||
47240             tg.toLowerCase() == 'sup' ||
47241             tg.toLowerCase() == 'sub' 
47242             ) {
47243             
47244             range = this.createRange(this.getSelection());
47245             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47246             wrappingNode.appendChild(range.extractContents());
47247             range.insertNode(wrappingNode);
47248
47249             return;
47250             
47251             
47252             
47253         }
47254         this.execCmd("formatblock",   tg);
47255         this.undoManager.addEvent(); 
47256     },
47257     
47258     insertText : function(txt)
47259     {
47260         
47261         
47262         var range = this.createRange();
47263         range.deleteContents();
47264                //alert(Sender.getAttribute('label'));
47265                
47266         range.insertNode(this.doc.createTextNode(txt));
47267         this.undoManager.addEvent();
47268     } ,
47269     
47270      
47271
47272     /**
47273      * Executes a Midas editor command on the editor document and performs necessary focus and
47274      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47275      * @param {String} cmd The Midas command
47276      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47277      */
47278     relayCmd : function(cmd, value){
47279         this.win.focus();
47280         this.execCmd(cmd, value);
47281         this.owner.fireEvent('editorevent', this);
47282         //this.updateToolbar();
47283         this.owner.deferFocus();
47284     },
47285
47286     /**
47287      * Executes a Midas editor command directly on the editor document.
47288      * For visual commands, you should use {@link #relayCmd} instead.
47289      * <b>This should only be called after the editor is initialized.</b>
47290      * @param {String} cmd The Midas command
47291      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47292      */
47293     execCmd : function(cmd, value){
47294         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47295         this.syncValue();
47296     },
47297  
47298  
47299    
47300     /**
47301      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47302      * to insert tRoo.
47303      * @param {String} text | dom node.. 
47304      */
47305     insertAtCursor : function(text)
47306     {
47307         
47308         if(!this.activated){
47309             return;
47310         }
47311          
47312         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47313             this.win.focus();
47314             
47315             
47316             // from jquery ui (MIT licenced)
47317             var range, node;
47318             var win = this.win;
47319             
47320             if (win.getSelection && win.getSelection().getRangeAt) {
47321                 
47322                 // delete the existing?
47323                 
47324                 this.createRange(this.getSelection()).deleteContents();
47325                 range = win.getSelection().getRangeAt(0);
47326                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47327                 range.insertNode(node);
47328                 range = range.cloneRange();
47329                 range.collapse(false);
47330                  
47331                 win.getSelection().removeAllRanges();
47332                 win.getSelection().addRange(range);
47333                 
47334                 
47335                 
47336             } else if (win.document.selection && win.document.selection.createRange) {
47337                 // no firefox support
47338                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47339                 win.document.selection.createRange().pasteHTML(txt);
47340             
47341             } else {
47342                 // no firefox support
47343                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47344                 this.execCmd('InsertHTML', txt);
47345             } 
47346             this.syncValue();
47347             
47348             this.deferFocus();
47349         }
47350     },
47351  // private
47352     mozKeyPress : function(e){
47353         if(e.ctrlKey){
47354             var c = e.getCharCode(), cmd;
47355           
47356             if(c > 0){
47357                 c = String.fromCharCode(c).toLowerCase();
47358                 switch(c){
47359                     case 'b':
47360                         cmd = 'bold';
47361                         break;
47362                     case 'i':
47363                         cmd = 'italic';
47364                         break;
47365                     
47366                     case 'u':
47367                         cmd = 'underline';
47368                         break;
47369                     
47370                     //case 'v':
47371                       //  this.cleanUpPaste.defer(100, this);
47372                       //  return;
47373                         
47374                 }
47375                 if(cmd){
47376                     this.win.focus();
47377                     this.execCmd(cmd);
47378                     this.deferFocus();
47379                     e.preventDefault();
47380                 }
47381                 
47382             }
47383         }
47384     },
47385
47386     // private
47387     fixKeys : function(){ // load time branching for fastest keydown performance
47388         if(Roo.isIE){
47389             return function(e){
47390                 var k = e.getKey(), r;
47391                 if(k == e.TAB){
47392                     e.stopEvent();
47393                     r = this.doc.selection.createRange();
47394                     if(r){
47395                         r.collapse(true);
47396                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47397                         this.deferFocus();
47398                     }
47399                     return;
47400                 }
47401                 
47402                 if(k == e.ENTER){
47403                     r = this.doc.selection.createRange();
47404                     if(r){
47405                         var target = r.parentElement();
47406                         if(!target || target.tagName.toLowerCase() != 'li'){
47407                             e.stopEvent();
47408                             r.pasteHTML('<br/>');
47409                             r.collapse(false);
47410                             r.select();
47411                         }
47412                     }
47413                 }
47414                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47415                 //    this.cleanUpPaste.defer(100, this);
47416                 //    return;
47417                 //}
47418                 
47419                 
47420             };
47421         }else if(Roo.isOpera){
47422             return function(e){
47423                 var k = e.getKey();
47424                 if(k == e.TAB){
47425                     e.stopEvent();
47426                     this.win.focus();
47427                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47428                     this.deferFocus();
47429                 }
47430                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47431                 //    this.cleanUpPaste.defer(100, this);
47432                  //   return;
47433                 //}
47434                 
47435             };
47436         }else if(Roo.isSafari){
47437             return function(e){
47438                 var k = e.getKey();
47439                 
47440                 if(k == e.TAB){
47441                     e.stopEvent();
47442                     this.execCmd('InsertText','\t');
47443                     this.deferFocus();
47444                     return;
47445                 }
47446                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47447                  //   this.cleanUpPaste.defer(100, this);
47448                  //   return;
47449                // }
47450                 
47451              };
47452         }
47453     }(),
47454     
47455     getAllAncestors: function()
47456     {
47457         var p = this.getSelectedNode();
47458         var a = [];
47459         if (!p) {
47460             a.push(p); // push blank onto stack..
47461             p = this.getParentElement();
47462         }
47463         
47464         
47465         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47466             a.push(p);
47467             p = p.parentNode;
47468         }
47469         a.push(this.doc.body);
47470         return a;
47471     },
47472     lastSel : false,
47473     lastSelNode : false,
47474     
47475     
47476     getSelection : function() 
47477     {
47478         this.assignDocWin();
47479         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47480     },
47481     /**
47482      * Select a dom node
47483      * @param {DomElement} node the node to select
47484      */
47485     selectNode : function(node)
47486     {
47487         var nodeRange = node.ownerDocument.createRange();
47488         try {
47489             nodeRange.selectNode(node);
47490         } catch (e) {
47491             nodeRange.selectNodeContents(node);
47492         }
47493         //nodeRange.collapse(true);
47494         var s = this.win.getSelection();
47495         s.removeAllRanges();
47496         s.addRange(nodeRange);
47497     },
47498     
47499     getSelectedNode: function() 
47500     {
47501         // this may only work on Gecko!!!
47502         
47503         // should we cache this!!!!
47504         
47505         
47506         
47507          
47508         var range = this.createRange(this.getSelection()).cloneRange();
47509         
47510         if (Roo.isIE) {
47511             var parent = range.parentElement();
47512             while (true) {
47513                 var testRange = range.duplicate();
47514                 testRange.moveToElementText(parent);
47515                 if (testRange.inRange(range)) {
47516                     break;
47517                 }
47518                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47519                     break;
47520                 }
47521                 parent = parent.parentElement;
47522             }
47523             return parent;
47524         }
47525         
47526         // is ancestor a text element.
47527         var ac =  range.commonAncestorContainer;
47528         if (ac.nodeType == 3) {
47529             ac = ac.parentNode;
47530         }
47531         
47532         var ar = ac.childNodes;
47533          
47534         var nodes = [];
47535         var other_nodes = [];
47536         var has_other_nodes = false;
47537         for (var i=0;i<ar.length;i++) {
47538             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47539                 continue;
47540             }
47541             // fullly contained node.
47542             
47543             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47544                 nodes.push(ar[i]);
47545                 continue;
47546             }
47547             
47548             // probably selected..
47549             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47550                 other_nodes.push(ar[i]);
47551                 continue;
47552             }
47553             // outer..
47554             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47555                 continue;
47556             }
47557             
47558             
47559             has_other_nodes = true;
47560         }
47561         if (!nodes.length && other_nodes.length) {
47562             nodes= other_nodes;
47563         }
47564         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47565             return false;
47566         }
47567         
47568         return nodes[0];
47569     },
47570     createRange: function(sel)
47571     {
47572         // this has strange effects when using with 
47573         // top toolbar - not sure if it's a great idea.
47574         //this.editor.contentWindow.focus();
47575         if (typeof sel != "undefined") {
47576             try {
47577                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47578             } catch(e) {
47579                 return this.doc.createRange();
47580             }
47581         } else {
47582             return this.doc.createRange();
47583         }
47584     },
47585     getParentElement: function()
47586     {
47587         
47588         this.assignDocWin();
47589         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47590         
47591         var range = this.createRange(sel);
47592          
47593         try {
47594             var p = range.commonAncestorContainer;
47595             while (p.nodeType == 3) { // text node
47596                 p = p.parentNode;
47597             }
47598             return p;
47599         } catch (e) {
47600             return null;
47601         }
47602     
47603     },
47604     /***
47605      *
47606      * Range intersection.. the hard stuff...
47607      *  '-1' = before
47608      *  '0' = hits..
47609      *  '1' = after.
47610      *         [ -- selected range --- ]
47611      *   [fail]                        [fail]
47612      *
47613      *    basically..
47614      *      if end is before start or  hits it. fail.
47615      *      if start is after end or hits it fail.
47616      *
47617      *   if either hits (but other is outside. - then it's not 
47618      *   
47619      *    
47620      **/
47621     
47622     
47623     // @see http://www.thismuchiknow.co.uk/?p=64.
47624     rangeIntersectsNode : function(range, node)
47625     {
47626         var nodeRange = node.ownerDocument.createRange();
47627         try {
47628             nodeRange.selectNode(node);
47629         } catch (e) {
47630             nodeRange.selectNodeContents(node);
47631         }
47632     
47633         var rangeStartRange = range.cloneRange();
47634         rangeStartRange.collapse(true);
47635     
47636         var rangeEndRange = range.cloneRange();
47637         rangeEndRange.collapse(false);
47638     
47639         var nodeStartRange = nodeRange.cloneRange();
47640         nodeStartRange.collapse(true);
47641     
47642         var nodeEndRange = nodeRange.cloneRange();
47643         nodeEndRange.collapse(false);
47644     
47645         return rangeStartRange.compareBoundaryPoints(
47646                  Range.START_TO_START, nodeEndRange) == -1 &&
47647                rangeEndRange.compareBoundaryPoints(
47648                  Range.START_TO_START, nodeStartRange) == 1;
47649         
47650          
47651     },
47652     rangeCompareNode : function(range, node)
47653     {
47654         var nodeRange = node.ownerDocument.createRange();
47655         try {
47656             nodeRange.selectNode(node);
47657         } catch (e) {
47658             nodeRange.selectNodeContents(node);
47659         }
47660         
47661         
47662         range.collapse(true);
47663     
47664         nodeRange.collapse(true);
47665      
47666         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47667         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47668          
47669         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47670         
47671         var nodeIsBefore   =  ss == 1;
47672         var nodeIsAfter    = ee == -1;
47673         
47674         if (nodeIsBefore && nodeIsAfter) {
47675             return 0; // outer
47676         }
47677         if (!nodeIsBefore && nodeIsAfter) {
47678             return 1; //right trailed.
47679         }
47680         
47681         if (nodeIsBefore && !nodeIsAfter) {
47682             return 2;  // left trailed.
47683         }
47684         // fully contined.
47685         return 3;
47686     },
47687  
47688     cleanWordChars : function(input) {// change the chars to hex code
47689         
47690        var swapCodes  = [ 
47691             [    8211, "&#8211;" ], 
47692             [    8212, "&#8212;" ], 
47693             [    8216,  "'" ],  
47694             [    8217, "'" ],  
47695             [    8220, '"' ],  
47696             [    8221, '"' ],  
47697             [    8226, "*" ],  
47698             [    8230, "..." ]
47699         ]; 
47700         var output = input;
47701         Roo.each(swapCodes, function(sw) { 
47702             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47703             
47704             output = output.replace(swapper, sw[1]);
47705         });
47706         
47707         return output;
47708     },
47709     
47710      
47711     
47712         
47713     
47714     cleanUpChild : function (node)
47715     {
47716         
47717         new Roo.htmleditor.FilterComment({node : node});
47718         new Roo.htmleditor.FilterAttributes({
47719                 node : node,
47720                 attrib_black : this.ablack,
47721                 attrib_clean : this.aclean,
47722                 style_white : this.cwhite,
47723                 style_black : this.cblack
47724         });
47725         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47726         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47727          
47728         
47729     },
47730     
47731     /**
47732      * Clean up MS wordisms...
47733      * @deprecated - use filter directly
47734      */
47735     cleanWord : function(node)
47736     {
47737         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47738         
47739     },
47740    
47741     
47742     /**
47743
47744      * @deprecated - use filters
47745      */
47746     cleanTableWidths : function(node)
47747     {
47748         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47749         
47750  
47751     },
47752     
47753      
47754         
47755     applyBlacklists : function()
47756     {
47757         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47758         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47759         
47760         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47761         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47762         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47763         
47764         this.white = [];
47765         this.black = [];
47766         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47767             if (b.indexOf(tag) > -1) {
47768                 return;
47769             }
47770             this.white.push(tag);
47771             
47772         }, this);
47773         
47774         Roo.each(w, function(tag) {
47775             if (b.indexOf(tag) > -1) {
47776                 return;
47777             }
47778             if (this.white.indexOf(tag) > -1) {
47779                 return;
47780             }
47781             this.white.push(tag);
47782             
47783         }, this);
47784         
47785         
47786         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47787             if (w.indexOf(tag) > -1) {
47788                 return;
47789             }
47790             this.black.push(tag);
47791             
47792         }, this);
47793         
47794         Roo.each(b, function(tag) {
47795             if (w.indexOf(tag) > -1) {
47796                 return;
47797             }
47798             if (this.black.indexOf(tag) > -1) {
47799                 return;
47800             }
47801             this.black.push(tag);
47802             
47803         }, this);
47804         
47805         
47806         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47807         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47808         
47809         this.cwhite = [];
47810         this.cblack = [];
47811         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47812             if (b.indexOf(tag) > -1) {
47813                 return;
47814             }
47815             this.cwhite.push(tag);
47816             
47817         }, this);
47818         
47819         Roo.each(w, function(tag) {
47820             if (b.indexOf(tag) > -1) {
47821                 return;
47822             }
47823             if (this.cwhite.indexOf(tag) > -1) {
47824                 return;
47825             }
47826             this.cwhite.push(tag);
47827             
47828         }, this);
47829         
47830         
47831         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47832             if (w.indexOf(tag) > -1) {
47833                 return;
47834             }
47835             this.cblack.push(tag);
47836             
47837         }, this);
47838         
47839         Roo.each(b, function(tag) {
47840             if (w.indexOf(tag) > -1) {
47841                 return;
47842             }
47843             if (this.cblack.indexOf(tag) > -1) {
47844                 return;
47845             }
47846             this.cblack.push(tag);
47847             
47848         }, this);
47849     },
47850     
47851     setStylesheets : function(stylesheets)
47852     {
47853         if(typeof(stylesheets) == 'string'){
47854             Roo.get(this.iframe.contentDocument.head).createChild({
47855                 tag : 'link',
47856                 rel : 'stylesheet',
47857                 type : 'text/css',
47858                 href : stylesheets
47859             });
47860             
47861             return;
47862         }
47863         var _this = this;
47864      
47865         Roo.each(stylesheets, function(s) {
47866             if(!s.length){
47867                 return;
47868             }
47869             
47870             Roo.get(_this.iframe.contentDocument.head).createChild({
47871                 tag : 'link',
47872                 rel : 'stylesheet',
47873                 type : 'text/css',
47874                 href : s
47875             });
47876         });
47877
47878         
47879     },
47880     
47881     removeStylesheets : function()
47882     {
47883         var _this = this;
47884         
47885         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47886             s.remove();
47887         });
47888     },
47889     
47890     setStyle : function(style)
47891     {
47892         Roo.get(this.iframe.contentDocument.head).createChild({
47893             tag : 'style',
47894             type : 'text/css',
47895             html : style
47896         });
47897
47898         return;
47899     }
47900     
47901     // hide stuff that is not compatible
47902     /**
47903      * @event blur
47904      * @hide
47905      */
47906     /**
47907      * @event change
47908      * @hide
47909      */
47910     /**
47911      * @event focus
47912      * @hide
47913      */
47914     /**
47915      * @event specialkey
47916      * @hide
47917      */
47918     /**
47919      * @cfg {String} fieldClass @hide
47920      */
47921     /**
47922      * @cfg {String} focusClass @hide
47923      */
47924     /**
47925      * @cfg {String} autoCreate @hide
47926      */
47927     /**
47928      * @cfg {String} inputType @hide
47929      */
47930     /**
47931      * @cfg {String} invalidClass @hide
47932      */
47933     /**
47934      * @cfg {String} invalidText @hide
47935      */
47936     /**
47937      * @cfg {String} msgFx @hide
47938      */
47939     /**
47940      * @cfg {String} validateOnBlur @hide
47941      */
47942 });
47943
47944 Roo.HtmlEditorCore.white = [
47945         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47946         
47947        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47948        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47949        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47950        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47951        'TABLE',   'UL',         'XMP', 
47952        
47953        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47954       'THEAD',   'TR', 
47955      
47956       'DIR', 'MENU', 'OL', 'UL', 'DL',
47957        
47958       'EMBED',  'OBJECT'
47959 ];
47960
47961
47962 Roo.HtmlEditorCore.black = [
47963     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47964         'APPLET', // 
47965         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47966         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47967         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47968         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47969         //'FONT' // CLEAN LATER..
47970         'COLGROUP', 'COL'  // messy tables.
47971         
47972 ];
47973 Roo.HtmlEditorCore.clean = [ // ?? needed???
47974      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47975 ];
47976 Roo.HtmlEditorCore.tag_remove = [
47977     'FONT', 'TBODY'  
47978 ];
47979 // attributes..
47980
47981 Roo.HtmlEditorCore.ablack = [
47982     'on'
47983 ];
47984     
47985 Roo.HtmlEditorCore.aclean = [ 
47986     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47987 ];
47988
47989 // protocols..
47990 Roo.HtmlEditorCore.pwhite= [
47991         'http',  'https',  'mailto'
47992 ];
47993
47994 // white listed style attributes.
47995 Roo.HtmlEditorCore.cwhite= [
47996       //  'text-align', /// default is to allow most things..
47997       
47998          
47999 //        'font-size'//??
48000 ];
48001
48002 // black listed style attributes.
48003 Roo.HtmlEditorCore.cblack= [
48004       //  'font-size' -- this can be set by the project 
48005 ];
48006
48007
48008
48009
48010     //<script type="text/javascript">
48011
48012 /*
48013  * Ext JS Library 1.1.1
48014  * Copyright(c) 2006-2007, Ext JS, LLC.
48015  * Licence LGPL
48016  * 
48017  */
48018  
48019  
48020 Roo.form.HtmlEditor = function(config){
48021     
48022     
48023     
48024     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48025     
48026     if (!this.toolbars) {
48027         this.toolbars = [];
48028     }
48029     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48030     
48031     
48032 };
48033
48034 /**
48035  * @class Roo.form.HtmlEditor
48036  * @extends Roo.form.Field
48037  * Provides a lightweight HTML Editor component.
48038  *
48039  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48040  * 
48041  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48042  * supported by this editor.</b><br/><br/>
48043  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48044  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48045  */
48046 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48047     /**
48048      * @cfg {Boolean} clearUp
48049      */
48050     clearUp : true,
48051       /**
48052      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48053      */
48054     toolbars : false,
48055    
48056      /**
48057      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48058      *                        Roo.resizable.
48059      */
48060     resizable : false,
48061      /**
48062      * @cfg {Number} height (in pixels)
48063      */   
48064     height: 300,
48065    /**
48066      * @cfg {Number} width (in pixels)
48067      */   
48068     width: 500,
48069     
48070     /**
48071      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48072      * 
48073      */
48074     stylesheets: false,
48075     
48076     
48077      /**
48078      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48079      * 
48080      */
48081     cblack: false,
48082     /**
48083      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48084      * 
48085      */
48086     cwhite: false,
48087     
48088      /**
48089      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48090      * 
48091      */
48092     black: false,
48093     /**
48094      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48095      * 
48096      */
48097     white: false,
48098     /**
48099      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48100      */
48101     allowComments: false,
48102     /**
48103      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48104      */
48105     
48106     
48107      bodyCls : '',
48108     
48109     // id of frame..
48110     frameId: false,
48111     
48112     // private properties
48113     validationEvent : false,
48114     deferHeight: true,
48115     initialized : false,
48116     activated : false,
48117     
48118     onFocus : Roo.emptyFn,
48119     iframePad:3,
48120     hideMode:'offsets',
48121     
48122     actionMode : 'container', // defaults to hiding it...
48123     
48124     defaultAutoCreate : { // modified by initCompnoent..
48125         tag: "textarea",
48126         style:"width:500px;height:300px;",
48127         autocomplete: "new-password"
48128     },
48129
48130     // private
48131     initComponent : function(){
48132         this.addEvents({
48133             /**
48134              * @event initialize
48135              * Fires when the editor is fully initialized (including the iframe)
48136              * @param {HtmlEditor} this
48137              */
48138             initialize: true,
48139             /**
48140              * @event activate
48141              * Fires when the editor is first receives the focus. Any insertion must wait
48142              * until after this event.
48143              * @param {HtmlEditor} this
48144              */
48145             activate: true,
48146              /**
48147              * @event beforesync
48148              * Fires before the textarea is updated with content from the editor iframe. Return false
48149              * to cancel the sync.
48150              * @param {HtmlEditor} this
48151              * @param {String} html
48152              */
48153             beforesync: true,
48154              /**
48155              * @event beforepush
48156              * Fires before the iframe editor is updated with content from the textarea. Return false
48157              * to cancel the push.
48158              * @param {HtmlEditor} this
48159              * @param {String} html
48160              */
48161             beforepush: true,
48162              /**
48163              * @event sync
48164              * Fires when the textarea is updated with content from the editor iframe.
48165              * @param {HtmlEditor} this
48166              * @param {String} html
48167              */
48168             sync: true,
48169              /**
48170              * @event push
48171              * Fires when the iframe editor is updated with content from the textarea.
48172              * @param {HtmlEditor} this
48173              * @param {String} html
48174              */
48175             push: true,
48176              /**
48177              * @event editmodechange
48178              * Fires when the editor switches edit modes
48179              * @param {HtmlEditor} this
48180              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48181              */
48182             editmodechange: true,
48183             /**
48184              * @event editorevent
48185              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48186              * @param {HtmlEditor} this
48187              */
48188             editorevent: true,
48189             /**
48190              * @event firstfocus
48191              * Fires when on first focus - needed by toolbars..
48192              * @param {HtmlEditor} this
48193              */
48194             firstfocus: true,
48195             /**
48196              * @event autosave
48197              * Auto save the htmlEditor value as a file into Events
48198              * @param {HtmlEditor} this
48199              */
48200             autosave: true,
48201             /**
48202              * @event savedpreview
48203              * preview the saved version of htmlEditor
48204              * @param {HtmlEditor} this
48205              */
48206             savedpreview: true,
48207             
48208             /**
48209             * @event stylesheetsclick
48210             * Fires when press the Sytlesheets button
48211             * @param {Roo.HtmlEditorCore} this
48212             */
48213             stylesheetsclick: true,
48214             /**
48215             * @event paste
48216             * Fires when press user pastes into the editor
48217             * @param {Roo.HtmlEditorCore} this
48218             */
48219             paste: true 
48220         });
48221         this.defaultAutoCreate =  {
48222             tag: "textarea",
48223             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48224             autocomplete: "new-password"
48225         };
48226     },
48227
48228     /**
48229      * Protected method that will not generally be called directly. It
48230      * is called when the editor creates its toolbar. Override this method if you need to
48231      * add custom toolbar buttons.
48232      * @param {HtmlEditor} editor
48233      */
48234     createToolbar : function(editor){
48235         Roo.log("create toolbars");
48236         if (!editor.toolbars || !editor.toolbars.length) {
48237             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48238         }
48239         
48240         for (var i =0 ; i < editor.toolbars.length;i++) {
48241             editor.toolbars[i] = Roo.factory(
48242                     typeof(editor.toolbars[i]) == 'string' ?
48243                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48244                 Roo.form.HtmlEditor);
48245             editor.toolbars[i].init(editor);
48246         }
48247          
48248         
48249     },
48250
48251      
48252     // private
48253     onRender : function(ct, position)
48254     {
48255         var _t = this;
48256         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48257         
48258         this.wrap = this.el.wrap({
48259             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48260         });
48261         
48262         this.editorcore.onRender(ct, position);
48263          
48264         if (this.resizable) {
48265             this.resizeEl = new Roo.Resizable(this.wrap, {
48266                 pinned : true,
48267                 wrap: true,
48268                 dynamic : true,
48269                 minHeight : this.height,
48270                 height: this.height,
48271                 handles : this.resizable,
48272                 width: this.width,
48273                 listeners : {
48274                     resize : function(r, w, h) {
48275                         _t.onResize(w,h); // -something
48276                     }
48277                 }
48278             });
48279             
48280         }
48281         this.createToolbar(this);
48282        
48283         
48284         if(!this.width){
48285             this.setSize(this.wrap.getSize());
48286         }
48287         if (this.resizeEl) {
48288             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48289             // should trigger onReize..
48290         }
48291         
48292         this.keyNav = new Roo.KeyNav(this.el, {
48293             
48294             "tab" : function(e){
48295                 e.preventDefault();
48296                 
48297                 var value = this.getValue();
48298                 
48299                 var start = this.el.dom.selectionStart;
48300                 var end = this.el.dom.selectionEnd;
48301                 
48302                 if(!e.shiftKey){
48303                     
48304                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48305                     this.el.dom.setSelectionRange(end + 1, end + 1);
48306                     return;
48307                 }
48308                 
48309                 var f = value.substring(0, start).split("\t");
48310                 
48311                 if(f.pop().length != 0){
48312                     return;
48313                 }
48314                 
48315                 this.setValue(f.join("\t") + value.substring(end));
48316                 this.el.dom.setSelectionRange(start - 1, start - 1);
48317                 
48318             },
48319             
48320             "home" : function(e){
48321                 e.preventDefault();
48322                 
48323                 var curr = this.el.dom.selectionStart;
48324                 var lines = this.getValue().split("\n");
48325                 
48326                 if(!lines.length){
48327                     return;
48328                 }
48329                 
48330                 if(e.ctrlKey){
48331                     this.el.dom.setSelectionRange(0, 0);
48332                     return;
48333                 }
48334                 
48335                 var pos = 0;
48336                 
48337                 for (var i = 0; i < lines.length;i++) {
48338                     pos += lines[i].length;
48339                     
48340                     if(i != 0){
48341                         pos += 1;
48342                     }
48343                     
48344                     if(pos < curr){
48345                         continue;
48346                     }
48347                     
48348                     pos -= lines[i].length;
48349                     
48350                     break;
48351                 }
48352                 
48353                 if(!e.shiftKey){
48354                     this.el.dom.setSelectionRange(pos, pos);
48355                     return;
48356                 }
48357                 
48358                 this.el.dom.selectionStart = pos;
48359                 this.el.dom.selectionEnd = curr;
48360             },
48361             
48362             "end" : function(e){
48363                 e.preventDefault();
48364                 
48365                 var curr = this.el.dom.selectionStart;
48366                 var lines = this.getValue().split("\n");
48367                 
48368                 if(!lines.length){
48369                     return;
48370                 }
48371                 
48372                 if(e.ctrlKey){
48373                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48374                     return;
48375                 }
48376                 
48377                 var pos = 0;
48378                 
48379                 for (var i = 0; i < lines.length;i++) {
48380                     
48381                     pos += lines[i].length;
48382                     
48383                     if(i != 0){
48384                         pos += 1;
48385                     }
48386                     
48387                     if(pos < curr){
48388                         continue;
48389                     }
48390                     
48391                     break;
48392                 }
48393                 
48394                 if(!e.shiftKey){
48395                     this.el.dom.setSelectionRange(pos, pos);
48396                     return;
48397                 }
48398                 
48399                 this.el.dom.selectionStart = curr;
48400                 this.el.dom.selectionEnd = pos;
48401             },
48402
48403             scope : this,
48404
48405             doRelay : function(foo, bar, hname){
48406                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48407             },
48408
48409             forceKeyDown: true
48410         });
48411         
48412 //        if(this.autosave && this.w){
48413 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48414 //        }
48415     },
48416
48417     // private
48418     onResize : function(w, h)
48419     {
48420         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48421         var ew = false;
48422         var eh = false;
48423         
48424         if(this.el ){
48425             if(typeof w == 'number'){
48426                 var aw = w - this.wrap.getFrameWidth('lr');
48427                 this.el.setWidth(this.adjustWidth('textarea', aw));
48428                 ew = aw;
48429             }
48430             if(typeof h == 'number'){
48431                 var tbh = 0;
48432                 for (var i =0; i < this.toolbars.length;i++) {
48433                     // fixme - ask toolbars for heights?
48434                     tbh += this.toolbars[i].tb.el.getHeight();
48435                     if (this.toolbars[i].footer) {
48436                         tbh += this.toolbars[i].footer.el.getHeight();
48437                     }
48438                 }
48439                 
48440                 
48441                 
48442                 
48443                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48444                 ah -= 5; // knock a few pixes off for look..
48445 //                Roo.log(ah);
48446                 this.el.setHeight(this.adjustWidth('textarea', ah));
48447                 var eh = ah;
48448             }
48449         }
48450         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48451         this.editorcore.onResize(ew,eh);
48452         
48453     },
48454
48455     /**
48456      * Toggles the editor between standard and source edit mode.
48457      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48458      */
48459     toggleSourceEdit : function(sourceEditMode)
48460     {
48461         this.editorcore.toggleSourceEdit(sourceEditMode);
48462         
48463         if(this.editorcore.sourceEditMode){
48464             Roo.log('editor - showing textarea');
48465             
48466 //            Roo.log('in');
48467 //            Roo.log(this.syncValue());
48468             this.editorcore.syncValue();
48469             this.el.removeClass('x-hidden');
48470             this.el.dom.removeAttribute('tabIndex');
48471             this.el.focus();
48472             this.el.dom.scrollTop = 0;
48473             
48474             
48475             for (var i = 0; i < this.toolbars.length; i++) {
48476                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48477                     this.toolbars[i].tb.hide();
48478                     this.toolbars[i].footer.hide();
48479                 }
48480             }
48481             
48482         }else{
48483             Roo.log('editor - hiding textarea');
48484 //            Roo.log('out')
48485 //            Roo.log(this.pushValue()); 
48486             this.editorcore.pushValue();
48487             
48488             this.el.addClass('x-hidden');
48489             this.el.dom.setAttribute('tabIndex', -1);
48490             
48491             for (var i = 0; i < this.toolbars.length; i++) {
48492                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48493                     this.toolbars[i].tb.show();
48494                     this.toolbars[i].footer.show();
48495                 }
48496             }
48497             
48498             //this.deferFocus();
48499         }
48500         
48501         this.setSize(this.wrap.getSize());
48502         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48503         
48504         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48505     },
48506  
48507     // private (for BoxComponent)
48508     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48509
48510     // private (for BoxComponent)
48511     getResizeEl : function(){
48512         return this.wrap;
48513     },
48514
48515     // private (for BoxComponent)
48516     getPositionEl : function(){
48517         return this.wrap;
48518     },
48519
48520     // private
48521     initEvents : function(){
48522         this.originalValue = this.getValue();
48523     },
48524
48525     /**
48526      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48527      * @method
48528      */
48529     markInvalid : Roo.emptyFn,
48530     /**
48531      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48532      * @method
48533      */
48534     clearInvalid : Roo.emptyFn,
48535
48536     setValue : function(v){
48537         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48538         this.editorcore.pushValue();
48539     },
48540
48541      
48542     // private
48543     deferFocus : function(){
48544         this.focus.defer(10, this);
48545     },
48546
48547     // doc'ed in Field
48548     focus : function(){
48549         this.editorcore.focus();
48550         
48551     },
48552       
48553
48554     // private
48555     onDestroy : function(){
48556         
48557         
48558         
48559         if(this.rendered){
48560             
48561             for (var i =0; i < this.toolbars.length;i++) {
48562                 // fixme - ask toolbars for heights?
48563                 this.toolbars[i].onDestroy();
48564             }
48565             
48566             this.wrap.dom.innerHTML = '';
48567             this.wrap.remove();
48568         }
48569     },
48570
48571     // private
48572     onFirstFocus : function(){
48573         //Roo.log("onFirstFocus");
48574         this.editorcore.onFirstFocus();
48575          for (var i =0; i < this.toolbars.length;i++) {
48576             this.toolbars[i].onFirstFocus();
48577         }
48578         
48579     },
48580     
48581     // private
48582     syncValue : function()
48583     {
48584         this.editorcore.syncValue();
48585     },
48586     
48587     pushValue : function()
48588     {
48589         this.editorcore.pushValue();
48590     },
48591     
48592     setStylesheets : function(stylesheets)
48593     {
48594         this.editorcore.setStylesheets(stylesheets);
48595     },
48596     
48597     removeStylesheets : function()
48598     {
48599         this.editorcore.removeStylesheets();
48600     }
48601      
48602     
48603     // hide stuff that is not compatible
48604     /**
48605      * @event blur
48606      * @hide
48607      */
48608     /**
48609      * @event change
48610      * @hide
48611      */
48612     /**
48613      * @event focus
48614      * @hide
48615      */
48616     /**
48617      * @event specialkey
48618      * @hide
48619      */
48620     /**
48621      * @cfg {String} fieldClass @hide
48622      */
48623     /**
48624      * @cfg {String} focusClass @hide
48625      */
48626     /**
48627      * @cfg {String} autoCreate @hide
48628      */
48629     /**
48630      * @cfg {String} inputType @hide
48631      */
48632     /**
48633      * @cfg {String} invalidClass @hide
48634      */
48635     /**
48636      * @cfg {String} invalidText @hide
48637      */
48638     /**
48639      * @cfg {String} msgFx @hide
48640      */
48641     /**
48642      * @cfg {String} validateOnBlur @hide
48643      */
48644 });
48645  
48646     // <script type="text/javascript">
48647 /*
48648  * Based on
48649  * Ext JS Library 1.1.1
48650  * Copyright(c) 2006-2007, Ext JS, LLC.
48651  *  
48652  
48653  */
48654
48655 /**
48656  * @class Roo.form.HtmlEditorToolbar1
48657  * Basic Toolbar
48658  * 
48659  * Usage:
48660  *
48661  new Roo.form.HtmlEditor({
48662     ....
48663     toolbars : [
48664         new Roo.form.HtmlEditorToolbar1({
48665             disable : { fonts: 1 , format: 1, ..., ... , ...],
48666             btns : [ .... ]
48667         })
48668     }
48669      
48670  * 
48671  * @cfg {Object} disable List of elements to disable..
48672  * @cfg {Array} btns List of additional buttons.
48673  * 
48674  * 
48675  * NEEDS Extra CSS? 
48676  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48677  */
48678  
48679 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48680 {
48681     
48682     Roo.apply(this, config);
48683     
48684     // default disabled, based on 'good practice'..
48685     this.disable = this.disable || {};
48686     Roo.applyIf(this.disable, {
48687         fontSize : true,
48688         colors : true,
48689         specialElements : true
48690     });
48691     
48692     
48693     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48694     // dont call parent... till later.
48695 }
48696
48697 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48698     
48699     tb: false,
48700     
48701     rendered: false,
48702     
48703     editor : false,
48704     editorcore : false,
48705     /**
48706      * @cfg {Object} disable  List of toolbar elements to disable
48707          
48708      */
48709     disable : false,
48710     
48711     
48712      /**
48713      * @cfg {String} createLinkText The default text for the create link prompt
48714      */
48715     createLinkText : 'Please enter the URL for the link:',
48716     /**
48717      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48718      */
48719     defaultLinkValue : 'http:/'+'/',
48720    
48721     
48722       /**
48723      * @cfg {Array} fontFamilies An array of available font families
48724      */
48725     fontFamilies : [
48726         'Arial',
48727         'Courier New',
48728         'Tahoma',
48729         'Times New Roman',
48730         'Verdana'
48731     ],
48732     
48733     specialChars : [
48734            "&#169;",
48735           "&#174;",     
48736           "&#8482;",    
48737           "&#163;" ,    
48738          // "&#8212;",    
48739           "&#8230;",    
48740           "&#247;" ,    
48741         //  "&#225;" ,     ?? a acute?
48742            "&#8364;"    , //Euro
48743        //   "&#8220;"    ,
48744         //  "&#8221;"    ,
48745         //  "&#8226;"    ,
48746           "&#176;"  //   , // degrees
48747
48748          // "&#233;"     , // e ecute
48749          // "&#250;"     , // u ecute?
48750     ],
48751     
48752     specialElements : [
48753         {
48754             text: "Insert Table",
48755             xtype: 'MenuItem',
48756             xns : Roo.Menu,
48757             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48758                 
48759         },
48760         {    
48761             text: "Insert Image",
48762             xtype: 'MenuItem',
48763             xns : Roo.Menu,
48764             ihtml : '<img src="about:blank"/>'
48765             
48766         }
48767         
48768          
48769     ],
48770     
48771     
48772     inputElements : [ 
48773             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48774             "input:submit", "input:button", "select", "textarea", "label" ],
48775     formats : [
48776         ["p"] ,  
48777         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48778         ["pre"],[ "code"], 
48779         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48780         ['div'],['span'],
48781         ['sup'],['sub']
48782     ],
48783     
48784     cleanStyles : [
48785         "font-size"
48786     ],
48787      /**
48788      * @cfg {String} defaultFont default font to use.
48789      */
48790     defaultFont: 'tahoma',
48791    
48792     fontSelect : false,
48793     
48794     
48795     formatCombo : false,
48796     
48797     init : function(editor)
48798     {
48799         this.editor = editor;
48800         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48801         var editorcore = this.editorcore;
48802         
48803         var _t = this;
48804         
48805         var fid = editorcore.frameId;
48806         var etb = this;
48807         function btn(id, toggle, handler){
48808             var xid = fid + '-'+ id ;
48809             return {
48810                 id : xid,
48811                 cmd : id,
48812                 cls : 'x-btn-icon x-edit-'+id,
48813                 enableToggle:toggle !== false,
48814                 scope: _t, // was editor...
48815                 handler:handler||_t.relayBtnCmd,
48816                 clickEvent:'mousedown',
48817                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48818                 tabIndex:-1
48819             };
48820         }
48821         
48822         
48823         
48824         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48825         this.tb = tb;
48826          // stop form submits
48827         tb.el.on('click', function(e){
48828             e.preventDefault(); // what does this do?
48829         });
48830
48831         if(!this.disable.font) { // && !Roo.isSafari){
48832             /* why no safari for fonts 
48833             editor.fontSelect = tb.el.createChild({
48834                 tag:'select',
48835                 tabIndex: -1,
48836                 cls:'x-font-select',
48837                 html: this.createFontOptions()
48838             });
48839             
48840             editor.fontSelect.on('change', function(){
48841                 var font = editor.fontSelect.dom.value;
48842                 editor.relayCmd('fontname', font);
48843                 editor.deferFocus();
48844             }, editor);
48845             
48846             tb.add(
48847                 editor.fontSelect.dom,
48848                 '-'
48849             );
48850             */
48851             
48852         };
48853         if(!this.disable.formats){
48854             this.formatCombo = new Roo.form.ComboBox({
48855                 store: new Roo.data.SimpleStore({
48856                     id : 'tag',
48857                     fields: ['tag'],
48858                     data : this.formats // from states.js
48859                 }),
48860                 blockFocus : true,
48861                 name : '',
48862                 //autoCreate : {tag: "div",  size: "20"},
48863                 displayField:'tag',
48864                 typeAhead: false,
48865                 mode: 'local',
48866                 editable : false,
48867                 triggerAction: 'all',
48868                 emptyText:'Add tag',
48869                 selectOnFocus:true,
48870                 width:135,
48871                 listeners : {
48872                     'select': function(c, r, i) {
48873                         editorcore.insertTag(r.get('tag'));
48874                         editor.focus();
48875                     }
48876                 }
48877
48878             });
48879             tb.addField(this.formatCombo);
48880             
48881         }
48882         
48883         if(!this.disable.format){
48884             tb.add(
48885                 btn('bold'),
48886                 btn('italic'),
48887                 btn('underline'),
48888                 btn('strikethrough')
48889             );
48890         };
48891         if(!this.disable.fontSize){
48892             tb.add(
48893                 '-',
48894                 
48895                 
48896                 btn('increasefontsize', false, editorcore.adjustFont),
48897                 btn('decreasefontsize', false, editorcore.adjustFont)
48898             );
48899         };
48900         
48901         
48902         if(!this.disable.colors){
48903             tb.add(
48904                 '-', {
48905                     id:editorcore.frameId +'-forecolor',
48906                     cls:'x-btn-icon x-edit-forecolor',
48907                     clickEvent:'mousedown',
48908                     tooltip: this.buttonTips['forecolor'] || undefined,
48909                     tabIndex:-1,
48910                     menu : new Roo.menu.ColorMenu({
48911                         allowReselect: true,
48912                         focus: Roo.emptyFn,
48913                         value:'000000',
48914                         plain:true,
48915                         selectHandler: function(cp, color){
48916                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48917                             editor.deferFocus();
48918                         },
48919                         scope: editorcore,
48920                         clickEvent:'mousedown'
48921                     })
48922                 }, {
48923                     id:editorcore.frameId +'backcolor',
48924                     cls:'x-btn-icon x-edit-backcolor',
48925                     clickEvent:'mousedown',
48926                     tooltip: this.buttonTips['backcolor'] || undefined,
48927                     tabIndex:-1,
48928                     menu : new Roo.menu.ColorMenu({
48929                         focus: Roo.emptyFn,
48930                         value:'FFFFFF',
48931                         plain:true,
48932                         allowReselect: true,
48933                         selectHandler: function(cp, color){
48934                             if(Roo.isGecko){
48935                                 editorcore.execCmd('useCSS', false);
48936                                 editorcore.execCmd('hilitecolor', color);
48937                                 editorcore.execCmd('useCSS', true);
48938                                 editor.deferFocus();
48939                             }else{
48940                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48941                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48942                                 editor.deferFocus();
48943                             }
48944                         },
48945                         scope:editorcore,
48946                         clickEvent:'mousedown'
48947                     })
48948                 }
48949             );
48950         };
48951         // now add all the items...
48952         
48953
48954         if(!this.disable.alignments){
48955             tb.add(
48956                 '-',
48957                 btn('justifyleft'),
48958                 btn('justifycenter'),
48959                 btn('justifyright')
48960             );
48961         };
48962
48963         //if(!Roo.isSafari){
48964             if(!this.disable.links){
48965                 tb.add(
48966                     '-',
48967                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48968                 );
48969             };
48970
48971             if(!this.disable.lists){
48972                 tb.add(
48973                     '-',
48974                     btn('insertorderedlist'),
48975                     btn('insertunorderedlist')
48976                 );
48977             }
48978             if(!this.disable.sourceEdit){
48979                 tb.add(
48980                     '-',
48981                     btn('sourceedit', true, function(btn){
48982                         this.toggleSourceEdit(btn.pressed);
48983                     })
48984                 );
48985             }
48986         //}
48987         
48988         var smenu = { };
48989         // special menu.. - needs to be tidied up..
48990         if (!this.disable.special) {
48991             smenu = {
48992                 text: "&#169;",
48993                 cls: 'x-edit-none',
48994                 
48995                 menu : {
48996                     items : []
48997                 }
48998             };
48999             for (var i =0; i < this.specialChars.length; i++) {
49000                 smenu.menu.items.push({
49001                     
49002                     html: this.specialChars[i],
49003                     handler: function(a,b) {
49004                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
49005                         //editor.insertAtCursor(a.html);
49006                         
49007                     },
49008                     tabIndex:-1
49009                 });
49010             }
49011             
49012             
49013             tb.add(smenu);
49014             
49015             
49016         }
49017         
49018         var cmenu = { };
49019         if (!this.disable.cleanStyles) {
49020             cmenu = {
49021                 cls: 'x-btn-icon x-btn-clear',
49022                 
49023                 menu : {
49024                     items : []
49025                 }
49026             };
49027             for (var i =0; i < this.cleanStyles.length; i++) {
49028                 cmenu.menu.items.push({
49029                     actiontype : this.cleanStyles[i],
49030                     html: 'Remove ' + this.cleanStyles[i],
49031                     handler: function(a,b) {
49032 //                        Roo.log(a);
49033 //                        Roo.log(b);
49034                         var c = Roo.get(editorcore.doc.body);
49035                         c.select('[style]').each(function(s) {
49036                             s.dom.style.removeProperty(a.actiontype);
49037                         });
49038                         editorcore.syncValue();
49039                     },
49040                     tabIndex:-1
49041                 });
49042             }
49043             cmenu.menu.items.push({
49044                 actiontype : 'tablewidths',
49045                 html: 'Remove Table Widths',
49046                 handler: function(a,b) {
49047                     editorcore.cleanTableWidths();
49048                     editorcore.syncValue();
49049                 },
49050                 tabIndex:-1
49051             });
49052             cmenu.menu.items.push({
49053                 actiontype : 'word',
49054                 html: 'Remove MS Word Formating',
49055                 handler: function(a,b) {
49056                     editorcore.cleanWord();
49057                     editorcore.syncValue();
49058                 },
49059                 tabIndex:-1
49060             });
49061             
49062             cmenu.menu.items.push({
49063                 actiontype : 'all',
49064                 html: 'Remove All Styles',
49065                 handler: function(a,b) {
49066                     
49067                     var c = Roo.get(editorcore.doc.body);
49068                     c.select('[style]').each(function(s) {
49069                         s.dom.removeAttribute('style');
49070                     });
49071                     editorcore.syncValue();
49072                 },
49073                 tabIndex:-1
49074             });
49075             
49076             cmenu.menu.items.push({
49077                 actiontype : 'all',
49078                 html: 'Remove All CSS Classes',
49079                 handler: function(a,b) {
49080                     
49081                     var c = Roo.get(editorcore.doc.body);
49082                     c.select('[class]').each(function(s) {
49083                         s.dom.removeAttribute('class');
49084                     });
49085                     editorcore.cleanWord();
49086                     editorcore.syncValue();
49087                 },
49088                 tabIndex:-1
49089             });
49090             
49091              cmenu.menu.items.push({
49092                 actiontype : 'tidy',
49093                 html: 'Tidy HTML Source',
49094                 handler: function(a,b) {
49095                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49096                     editorcore.syncValue();
49097                 },
49098                 tabIndex:-1
49099             });
49100             
49101             
49102             tb.add(cmenu);
49103         }
49104          
49105         if (!this.disable.specialElements) {
49106             var semenu = {
49107                 text: "Other;",
49108                 cls: 'x-edit-none',
49109                 menu : {
49110                     items : []
49111                 }
49112             };
49113             for (var i =0; i < this.specialElements.length; i++) {
49114                 semenu.menu.items.push(
49115                     Roo.apply({ 
49116                         handler: function(a,b) {
49117                             editor.insertAtCursor(this.ihtml);
49118                         }
49119                     }, this.specialElements[i])
49120                 );
49121                     
49122             }
49123             
49124             tb.add(semenu);
49125             
49126             
49127         }
49128          
49129         
49130         if (this.btns) {
49131             for(var i =0; i< this.btns.length;i++) {
49132                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49133                 b.cls =  'x-edit-none';
49134                 
49135                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49136                     b.cls += ' x-init-enable';
49137                 }
49138                 
49139                 b.scope = editorcore;
49140                 tb.add(b);
49141             }
49142         
49143         }
49144         
49145         
49146         
49147         // disable everything...
49148         
49149         this.tb.items.each(function(item){
49150             
49151            if(
49152                 item.id != editorcore.frameId+ '-sourceedit' && 
49153                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49154             ){
49155                 
49156                 item.disable();
49157             }
49158         });
49159         this.rendered = true;
49160         
49161         // the all the btns;
49162         editor.on('editorevent', this.updateToolbar, this);
49163         // other toolbars need to implement this..
49164         //editor.on('editmodechange', this.updateToolbar, this);
49165     },
49166     
49167     
49168     relayBtnCmd : function(btn) {
49169         this.editorcore.relayCmd(btn.cmd);
49170     },
49171     // private used internally
49172     createLink : function(){
49173         Roo.log("create link?");
49174         var url = prompt(this.createLinkText, this.defaultLinkValue);
49175         if(url && url != 'http:/'+'/'){
49176             this.editorcore.relayCmd('createlink', url);
49177         }
49178     },
49179
49180     
49181     /**
49182      * Protected method that will not generally be called directly. It triggers
49183      * a toolbar update by reading the markup state of the current selection in the editor.
49184      */
49185     updateToolbar: function(){
49186
49187         if(!this.editorcore.activated){
49188             this.editor.onFirstFocus();
49189             return;
49190         }
49191
49192         var btns = this.tb.items.map, 
49193             doc = this.editorcore.doc,
49194             frameId = this.editorcore.frameId;
49195
49196         if(!this.disable.font && !Roo.isSafari){
49197             /*
49198             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49199             if(name != this.fontSelect.dom.value){
49200                 this.fontSelect.dom.value = name;
49201             }
49202             */
49203         }
49204         if(!this.disable.format){
49205             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49206             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49207             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49208             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49209         }
49210         if(!this.disable.alignments){
49211             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49212             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49213             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49214         }
49215         if(!Roo.isSafari && !this.disable.lists){
49216             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49217             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49218         }
49219         
49220         var ans = this.editorcore.getAllAncestors();
49221         if (this.formatCombo) {
49222             
49223             
49224             var store = this.formatCombo.store;
49225             this.formatCombo.setValue("");
49226             for (var i =0; i < ans.length;i++) {
49227                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49228                     // select it..
49229                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49230                     break;
49231                 }
49232             }
49233         }
49234         
49235         
49236         
49237         // hides menus... - so this cant be on a menu...
49238         Roo.menu.MenuMgr.hideAll();
49239
49240         //this.editorsyncValue();
49241     },
49242    
49243     
49244     createFontOptions : function(){
49245         var buf = [], fs = this.fontFamilies, ff, lc;
49246         
49247         
49248         
49249         for(var i = 0, len = fs.length; i< len; i++){
49250             ff = fs[i];
49251             lc = ff.toLowerCase();
49252             buf.push(
49253                 '<option value="',lc,'" style="font-family:',ff,';"',
49254                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49255                     ff,
49256                 '</option>'
49257             );
49258         }
49259         return buf.join('');
49260     },
49261     
49262     toggleSourceEdit : function(sourceEditMode){
49263         
49264         Roo.log("toolbar toogle");
49265         if(sourceEditMode === undefined){
49266             sourceEditMode = !this.sourceEditMode;
49267         }
49268         this.sourceEditMode = sourceEditMode === true;
49269         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49270         // just toggle the button?
49271         if(btn.pressed !== this.sourceEditMode){
49272             btn.toggle(this.sourceEditMode);
49273             return;
49274         }
49275         
49276         if(sourceEditMode){
49277             Roo.log("disabling buttons");
49278             this.tb.items.each(function(item){
49279                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49280                     item.disable();
49281                 }
49282             });
49283           
49284         }else{
49285             Roo.log("enabling buttons");
49286             if(this.editorcore.initialized){
49287                 this.tb.items.each(function(item){
49288                     item.enable();
49289                 });
49290             }
49291             
49292         }
49293         Roo.log("calling toggole on editor");
49294         // tell the editor that it's been pressed..
49295         this.editor.toggleSourceEdit(sourceEditMode);
49296        
49297     },
49298      /**
49299      * Object collection of toolbar tooltips for the buttons in the editor. The key
49300      * is the command id associated with that button and the value is a valid QuickTips object.
49301      * For example:
49302 <pre><code>
49303 {
49304     bold : {
49305         title: 'Bold (Ctrl+B)',
49306         text: 'Make the selected text bold.',
49307         cls: 'x-html-editor-tip'
49308     },
49309     italic : {
49310         title: 'Italic (Ctrl+I)',
49311         text: 'Make the selected text italic.',
49312         cls: 'x-html-editor-tip'
49313     },
49314     ...
49315 </code></pre>
49316     * @type Object
49317      */
49318     buttonTips : {
49319         bold : {
49320             title: 'Bold (Ctrl+B)',
49321             text: 'Make the selected text bold.',
49322             cls: 'x-html-editor-tip'
49323         },
49324         italic : {
49325             title: 'Italic (Ctrl+I)',
49326             text: 'Make the selected text italic.',
49327             cls: 'x-html-editor-tip'
49328         },
49329         underline : {
49330             title: 'Underline (Ctrl+U)',
49331             text: 'Underline the selected text.',
49332             cls: 'x-html-editor-tip'
49333         },
49334         strikethrough : {
49335             title: 'Strikethrough',
49336             text: 'Strikethrough the selected text.',
49337             cls: 'x-html-editor-tip'
49338         },
49339         increasefontsize : {
49340             title: 'Grow Text',
49341             text: 'Increase the font size.',
49342             cls: 'x-html-editor-tip'
49343         },
49344         decreasefontsize : {
49345             title: 'Shrink Text',
49346             text: 'Decrease the font size.',
49347             cls: 'x-html-editor-tip'
49348         },
49349         backcolor : {
49350             title: 'Text Highlight Color',
49351             text: 'Change the background color of the selected text.',
49352             cls: 'x-html-editor-tip'
49353         },
49354         forecolor : {
49355             title: 'Font Color',
49356             text: 'Change the color of the selected text.',
49357             cls: 'x-html-editor-tip'
49358         },
49359         justifyleft : {
49360             title: 'Align Text Left',
49361             text: 'Align text to the left.',
49362             cls: 'x-html-editor-tip'
49363         },
49364         justifycenter : {
49365             title: 'Center Text',
49366             text: 'Center text in the editor.',
49367             cls: 'x-html-editor-tip'
49368         },
49369         justifyright : {
49370             title: 'Align Text Right',
49371             text: 'Align text to the right.',
49372             cls: 'x-html-editor-tip'
49373         },
49374         insertunorderedlist : {
49375             title: 'Bullet List',
49376             text: 'Start a bulleted list.',
49377             cls: 'x-html-editor-tip'
49378         },
49379         insertorderedlist : {
49380             title: 'Numbered List',
49381             text: 'Start a numbered list.',
49382             cls: 'x-html-editor-tip'
49383         },
49384         createlink : {
49385             title: 'Hyperlink',
49386             text: 'Make the selected text a hyperlink.',
49387             cls: 'x-html-editor-tip'
49388         },
49389         sourceedit : {
49390             title: 'Source Edit',
49391             text: 'Switch to source editing mode.',
49392             cls: 'x-html-editor-tip'
49393         }
49394     },
49395     // private
49396     onDestroy : function(){
49397         if(this.rendered){
49398             
49399             this.tb.items.each(function(item){
49400                 if(item.menu){
49401                     item.menu.removeAll();
49402                     if(item.menu.el){
49403                         item.menu.el.destroy();
49404                     }
49405                 }
49406                 item.destroy();
49407             });
49408              
49409         }
49410     },
49411     onFirstFocus: function() {
49412         this.tb.items.each(function(item){
49413            item.enable();
49414         });
49415     }
49416 });
49417
49418
49419
49420
49421 // <script type="text/javascript">
49422 /*
49423  * Based on
49424  * Ext JS Library 1.1.1
49425  * Copyright(c) 2006-2007, Ext JS, LLC.
49426  *  
49427  
49428  */
49429
49430  
49431 /**
49432  * @class Roo.form.HtmlEditor.ToolbarContext
49433  * Context Toolbar
49434  * 
49435  * Usage:
49436  *
49437  new Roo.form.HtmlEditor({
49438     ....
49439     toolbars : [
49440         { xtype: 'ToolbarStandard', styles : {} }
49441         { xtype: 'ToolbarContext', disable : {} }
49442     ]
49443 })
49444
49445      
49446  * 
49447  * @config : {Object} disable List of elements to disable.. (not done yet.)
49448  * @config : {Object} styles  Map of styles available.
49449  * 
49450  */
49451
49452 Roo.form.HtmlEditor.ToolbarContext = function(config)
49453 {
49454     
49455     Roo.apply(this, config);
49456     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49457     // dont call parent... till later.
49458     this.styles = this.styles || {};
49459 }
49460
49461  
49462
49463 Roo.form.HtmlEditor.ToolbarContext.types = {
49464     'IMG' : [
49465         {
49466             name : 'width',
49467             title: "Width",
49468             width: 40
49469         },
49470         {
49471             name : 'height',
49472             title: "Height",
49473             width: 40
49474         },
49475         {
49476             name : 'align',
49477             title: "Align",
49478             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49479             width : 80
49480             
49481         },
49482         {
49483             name : 'border',
49484             title: "Border",
49485             width: 40
49486         },
49487         {
49488             name : 'alt',
49489             title: "Alt",
49490             width: 120
49491         },
49492         {
49493             name : 'src',
49494             title: "Src",
49495             width: 220
49496         }
49497         
49498     ],
49499     
49500     'FIGURE' : [
49501         {
49502             name : 'align',
49503             title: "Align",
49504             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49505             width : 80  
49506         }
49507     ],
49508     'A' : [
49509         {
49510             name : 'name',
49511             title: "Name",
49512             width: 50
49513         },
49514         {
49515             name : 'target',
49516             title: "Target",
49517             width: 120
49518         },
49519         {
49520             name : 'href',
49521             title: "Href",
49522             width: 220
49523         } // border?
49524         
49525     ],
49526     
49527     'INPUT' : [
49528         {
49529             name : 'name',
49530             title: "name",
49531             width: 120
49532         },
49533         {
49534             name : 'value',
49535             title: "Value",
49536             width: 120
49537         },
49538         {
49539             name : 'width',
49540             title: "Width",
49541             width: 40
49542         }
49543     ],
49544     'LABEL' : [
49545          {
49546             name : 'for',
49547             title: "For",
49548             width: 120
49549         }
49550     ],
49551     'TEXTAREA' : [
49552         {
49553             name : 'name',
49554             title: "name",
49555             width: 120
49556         },
49557         {
49558             name : 'rows',
49559             title: "Rows",
49560             width: 20
49561         },
49562         {
49563             name : 'cols',
49564             title: "Cols",
49565             width: 20
49566         }
49567     ],
49568     'SELECT' : [
49569         {
49570             name : 'name',
49571             title: "name",
49572             width: 120
49573         },
49574         {
49575             name : 'selectoptions',
49576             title: "Options",
49577             width: 200
49578         }
49579     ],
49580     
49581     // should we really allow this??
49582     // should this just be 
49583     'BODY' : [
49584         
49585         {
49586             name : 'title',
49587             title: "Title",
49588             width: 200,
49589             disabled : true
49590         }
49591     ],
49592  
49593     '*' : [
49594         // empty.
49595     ]
49596
49597 };
49598
49599 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49600 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49601
49602 Roo.form.HtmlEditor.ToolbarContext.options = {
49603         'font-family'  : [ 
49604                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49605                 [ 'Courier New', 'Courier New'],
49606                 [ 'Tahoma', 'Tahoma'],
49607                 [ 'Times New Roman,serif', 'Times'],
49608                 [ 'Verdana','Verdana' ]
49609         ]
49610 };
49611
49612 // fixme - these need to be configurable..
49613  
49614
49615 //Roo.form.HtmlEditor.ToolbarContext.types
49616
49617
49618 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49619     
49620     tb: false,
49621     
49622     rendered: false,
49623     
49624     editor : false,
49625     editorcore : false,
49626     /**
49627      * @cfg {Object} disable  List of toolbar elements to disable
49628          
49629      */
49630     disable : false,
49631     /**
49632      * @cfg {Object} styles List of styles 
49633      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49634      *
49635      * These must be defined in the page, so they get rendered correctly..
49636      * .headline { }
49637      * TD.underline { }
49638      * 
49639      */
49640     styles : false,
49641     
49642     options: false,
49643     
49644     toolbars : false,
49645     
49646     init : function(editor)
49647     {
49648         this.editor = editor;
49649         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49650         var editorcore = this.editorcore;
49651         
49652         var fid = editorcore.frameId;
49653         var etb = this;
49654         function btn(id, toggle, handler){
49655             var xid = fid + '-'+ id ;
49656             return {
49657                 id : xid,
49658                 cmd : id,
49659                 cls : 'x-btn-icon x-edit-'+id,
49660                 enableToggle:toggle !== false,
49661                 scope: editorcore, // was editor...
49662                 handler:handler||editorcore.relayBtnCmd,
49663                 clickEvent:'mousedown',
49664                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49665                 tabIndex:-1
49666             };
49667         }
49668         // create a new element.
49669         var wdiv = editor.wrap.createChild({
49670                 tag: 'div'
49671             }, editor.wrap.dom.firstChild.nextSibling, true);
49672         
49673         // can we do this more than once??
49674         
49675          // stop form submits
49676       
49677  
49678         // disable everything...
49679         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49680         this.toolbars = {};
49681            
49682         for (var i in  ty) {
49683           
49684             this.toolbars[i] = this.buildToolbar(ty[i],i);
49685         }
49686         this.tb = this.toolbars.BODY;
49687         this.tb.el.show();
49688         this.buildFooter();
49689         this.footer.show();
49690         editor.on('hide', function( ) { this.footer.hide() }, this);
49691         editor.on('show', function( ) { this.footer.show() }, this);
49692         
49693          
49694         this.rendered = true;
49695         
49696         // the all the btns;
49697         editor.on('editorevent', this.updateToolbar, this);
49698         // other toolbars need to implement this..
49699         //editor.on('editmodechange', this.updateToolbar, this);
49700     },
49701     
49702     
49703     
49704     /**
49705      * Protected method that will not generally be called directly. It triggers
49706      * a toolbar update by reading the markup state of the current selection in the editor.
49707      *
49708      * Note you can force an update by calling on('editorevent', scope, false)
49709      */
49710     updateToolbar: function(editor ,ev, sel)
49711     {
49712         
49713         if (ev) {
49714             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49715         }
49716         
49717         //Roo.log(ev);
49718         // capture mouse up - this is handy for selecting images..
49719         // perhaps should go somewhere else...
49720         if(!this.editorcore.activated){
49721              this.editor.onFirstFocus();
49722             return;
49723         }
49724         //Roo.log(ev ? ev.target : 'NOTARGET');
49725         
49726         
49727         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49728         // selectNode - might want to handle IE?
49729         
49730         
49731         
49732         if (ev &&
49733             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49734             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49735             // they have click on an image...
49736             // let's see if we can change the selection...
49737             sel = ev.target;
49738             
49739             // this triggers looping?
49740             //this.editorcore.selectNode(sel);
49741              
49742         }  
49743         Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
49744         //Roo.get(node).addClass('roo-ed-selection');
49745       
49746         //var updateFooter = sel ? false : true; 
49747         
49748         
49749         var ans = this.editorcore.getAllAncestors();
49750         
49751         // pick
49752         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49753         
49754         if (!sel) { 
49755             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49756             sel = sel ? sel : this.editorcore.doc.body;
49757             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49758             
49759         }
49760         
49761         var tn = sel.tagName.toUpperCase();
49762         var lastSel = this.tb.selectedNode;
49763         this.tb.selectedNode = sel;
49764         var left_label = tn;
49765         
49766         // ok see if we are editing a block?
49767         
49768         var db = false;
49769         // you are not actually selecting the block.
49770         if (sel && sel.hasAttribute('data-block')) {
49771             db = sel;
49772         } else if (sel && !sel.hasAttribute('contenteditable')) {
49773             var sel_el = Roo.get(sel);
49774             db = sel_el.findParent('[data-block]');
49775             var cepar = sel_el.findParent('[contenteditable=true]');
49776             if (db && cepar && cepar.tagName != 'BODY') {
49777                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49778             }   
49779         }
49780         
49781         
49782         var block = false;
49783         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49784         if (db) {
49785             block = Roo.htmleditor.Block.factory(db);
49786             
49787             
49788             if (block) {
49789                 db.className +=  ' roo-ed-selection'; // since we removed it earlier... its not there..
49790                 tn = 'BLOCK.' + db.getAttribute('data-block');
49791                 
49792                 //this.editorcore.selectNode(db);
49793                 if (typeof(this.toolbars[tn]) == 'undefined') {
49794                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49795                 }
49796                 this.toolbars[tn].selectedNode = db;
49797                 left_label = block.friendly_name;
49798                 ans = this.editorcore.getAllAncestors();
49799             }
49800             
49801                 
49802             
49803         }
49804         
49805         
49806         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49807             return; // no change?
49808         }
49809         
49810         
49811           
49812         this.tb.el.hide();
49813         ///console.log("show: " + tn);
49814         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49815         
49816         this.tb.el.show();
49817         // update name
49818         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49819         
49820         
49821         // update attributes
49822         if (block && this.tb.fields) {
49823              
49824             this.tb.fields.each(function(e) {
49825                 e.setValue(block[e.name]);
49826             });
49827             
49828             
49829         } else  if (this.tb.fields && this.tb.selectedNode) {
49830             this.tb.fields.each( function(e) {
49831                 if (e.stylename) {
49832                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49833                     return;
49834                 } 
49835                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49836             }, this);
49837             this.updateToolbarStyles(this.tb.selectedNode);  
49838         }
49839         
49840         
49841        
49842         Roo.menu.MenuMgr.hideAll();
49843
49844         
49845         
49846     
49847         // update the footer
49848         //
49849         this.updateFooter(ans);
49850              
49851     },
49852     
49853     updateToolbarStyles : function(sel)
49854     {
49855         var hasStyles = false;
49856         for(var i in this.styles) {
49857             hasStyles = true;
49858             break;
49859         }
49860         
49861         // update styles
49862         if (hasStyles && this.tb.hasStyles) { 
49863             var st = this.tb.fields.item(0);
49864             
49865             st.store.removeAll();
49866             var cn = sel.className.split(/\s+/);
49867             
49868             var avs = [];
49869             if (this.styles['*']) {
49870                 
49871                 Roo.each(this.styles['*'], function(v) {
49872                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49873                 });
49874             }
49875             if (this.styles[tn]) { 
49876                 Roo.each(this.styles[tn], function(v) {
49877                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49878                 });
49879             }
49880             
49881             st.store.loadData(avs);
49882             st.collapse();
49883             st.setValue(cn);
49884         }
49885     },
49886     
49887      
49888     updateFooter : function(ans)
49889     {
49890         var html = '';
49891         if (ans === false) {
49892             this.footDisp.dom.innerHTML = '';
49893             return;
49894         }
49895         
49896         this.footerEls = ans.reverse();
49897         Roo.each(this.footerEls, function(a,i) {
49898             if (!a) { return; }
49899             html += html.length ? ' &gt; '  :  '';
49900             
49901             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49902             
49903         });
49904        
49905         // 
49906         var sz = this.footDisp.up('td').getSize();
49907         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49908         this.footDisp.dom.style.marginLeft = '5px';
49909         
49910         this.footDisp.dom.style.overflow = 'hidden';
49911         
49912         this.footDisp.dom.innerHTML = html;
49913             
49914         
49915     },
49916    
49917        
49918     // private
49919     onDestroy : function(){
49920         if(this.rendered){
49921             
49922             this.tb.items.each(function(item){
49923                 if(item.menu){
49924                     item.menu.removeAll();
49925                     if(item.menu.el){
49926                         item.menu.el.destroy();
49927                     }
49928                 }
49929                 item.destroy();
49930             });
49931              
49932         }
49933     },
49934     onFirstFocus: function() {
49935         // need to do this for all the toolbars..
49936         this.tb.items.each(function(item){
49937            item.enable();
49938         });
49939     },
49940     buildToolbar: function(tlist, nm, friendly_name, block)
49941     {
49942         var editor = this.editor;
49943         var editorcore = this.editorcore;
49944          // create a new element.
49945         var wdiv = editor.wrap.createChild({
49946                 tag: 'div'
49947             }, editor.wrap.dom.firstChild.nextSibling, true);
49948         
49949        
49950         var tb = new Roo.Toolbar(wdiv);
49951         ///this.tb = tb; // << this sets the active toolbar..
49952         if (tlist === false && block) {
49953             tlist = block.contextMenu(this);
49954         }
49955         
49956         tb.hasStyles = false;
49957         tb.name = nm;
49958         
49959         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49960         
49961         var styles = Array.from(this.styles);
49962         
49963         
49964         // styles...
49965         if (styles && styles.length) {
49966             tb.hasStyles = true;
49967             // this needs a multi-select checkbox...
49968             tb.addField( new Roo.form.ComboBox({
49969                 store: new Roo.data.SimpleStore({
49970                     id : 'val',
49971                     fields: ['val', 'selected'],
49972                     data : [] 
49973                 }),
49974                 name : '-roo-edit-className',
49975                 attrname : 'className',
49976                 displayField: 'val',
49977                 typeAhead: false,
49978                 mode: 'local',
49979                 editable : false,
49980                 triggerAction: 'all',
49981                 emptyText:'Select Style',
49982                 selectOnFocus:true,
49983                 width: 130,
49984                 listeners : {
49985                     'select': function(c, r, i) {
49986                         // initial support only for on class per el..
49987                         tb.selectedNode.className =  r ? r.get('val') : '';
49988                         editorcore.syncValue();
49989                     }
49990                 }
49991     
49992             }));
49993         }
49994         
49995         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49996         
49997         
49998         for (var i = 0; i < tlist.length; i++) {
49999             
50000             // newer versions will use xtype cfg to create menus.
50001             if (typeof(tlist[i].xtype) != 'undefined') {
50002                 
50003                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
50004                 
50005                 
50006                 continue;
50007             }
50008             
50009             var item = tlist[i];
50010             tb.add(item.title + ":&nbsp;");
50011             
50012             
50013             //optname == used so you can configure the options available..
50014             var opts = item.opts ? item.opts : false;
50015             if (item.optname) { // use the b
50016                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50017            
50018             }
50019             
50020             if (opts) {
50021                 // opts == pulldown..
50022                 tb.addField( new Roo.form.ComboBox({
50023                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50024                         id : 'val',
50025                         fields: ['val', 'display'],
50026                         data : opts  
50027                     }),
50028                     name : '-roo-edit-' + tlist[i].name,
50029                     
50030                     attrname : tlist[i].name,
50031                     stylename : item.style ? item.style : false,
50032                     
50033                     displayField: item.displayField ? item.displayField : 'val',
50034                     valueField :  'val',
50035                     typeAhead: false,
50036                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50037                     editable : false,
50038                     triggerAction: 'all',
50039                     emptyText:'Select',
50040                     selectOnFocus:true,
50041                     width: item.width ? item.width  : 130,
50042                     listeners : {
50043                         'select': function(c, r, i) {
50044                             if (tb.selectedNode.hasAttribute('data-block')) {
50045                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50046                                 b[c.attrname] = r.get('val');
50047                                 b.updateElement(tb.selectedNode);
50048                                 editorcore.syncValue();
50049                                 return;
50050                             }
50051                             
50052                             if (c.stylename) {
50053                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50054                                 editorcore.syncValue();
50055                                 return;
50056                             }
50057                             if (r === false) {
50058                                 tb.selectedNode.removeAttribute(c.attrname);
50059                                 editorcore.syncValue();
50060                                 return;
50061                             }
50062                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50063                             editorcore.syncValue();
50064                         }
50065                     }
50066
50067                 }));
50068                 continue;
50069                     
50070                  
50071                 /*
50072                 tb.addField( new Roo.form.TextField({
50073                     name: i,
50074                     width: 100,
50075                     //allowBlank:false,
50076                     value: ''
50077                 }));
50078                 continue;
50079                 */
50080             }
50081             tb.addField( new Roo.form.TextField({
50082                 name: '-roo-edit-' + tlist[i].name,
50083                 attrname : tlist[i].name,
50084                 
50085                 width: item.width,
50086                 //allowBlank:true,
50087                 value: '',
50088                 listeners: {
50089                     'change' : function(f, nv, ov) {
50090                         
50091                         if (tb.selectedNode.hasAttribute('data-block')) {
50092                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50093                             b[f.attrname] = nv;
50094                             b.updateElement(tb.selectedNode);
50095                             editorcore.syncValue();
50096                             return;
50097                         }
50098                         
50099                         tb.selectedNode.setAttribute(f.attrname, nv);
50100                         editorcore.syncValue();
50101                     }
50102                 }
50103             }));
50104              
50105         }
50106         
50107         var _this = this;
50108         
50109         if(nm == 'BODY'){
50110             tb.addSeparator();
50111         
50112             tb.addButton( {
50113                 text: 'Stylesheets',
50114
50115                 listeners : {
50116                     click : function ()
50117                     {
50118                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50119                     }
50120                 }
50121             });
50122         }
50123         
50124         tb.addFill();
50125         tb.addButton({
50126             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50127     
50128             listeners : {
50129                 click : function ()
50130                 {
50131                     var sn = tb.selectedNode;
50132                     if (block) {
50133                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50134                         
50135                     }
50136                     if (!sn) {
50137                         return;
50138                     }
50139                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50140                     if (sn.hasAttribute('data-block')) {
50141                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50142                         sn.parentNode.removeChild(sn);
50143                         
50144                     } else if (sn && sn.tagName != 'BODY') {
50145                         // remove and keep parents.
50146                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50147                         a.removeTag(sn);
50148                     }
50149                     
50150                     
50151                     var range = editorcore.createRange();
50152         
50153                     range.setStart(stn,0);
50154                     range.setEnd(stn,0); 
50155                     var selection = editorcore.getSelection();
50156                     selection.removeAllRanges();
50157                     selection.addRange(range);
50158                     
50159                     
50160                     //_this.updateToolbar(null, null, pn);
50161                     _this.updateToolbar(null, null, null);
50162                     _this.updateFooter(false);
50163                     
50164                 }
50165             }
50166             
50167                     
50168                 
50169             
50170         });
50171         
50172         
50173         tb.el.on('click', function(e){
50174             e.preventDefault(); // what does this do?
50175         });
50176         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50177         tb.el.hide();
50178         
50179         // dont need to disable them... as they will get hidden
50180         return tb;
50181          
50182         
50183     },
50184     buildFooter : function()
50185     {
50186         
50187         var fel = this.editor.wrap.createChild();
50188         this.footer = new Roo.Toolbar(fel);
50189         // toolbar has scrolly on left / right?
50190         var footDisp= new Roo.Toolbar.Fill();
50191         var _t = this;
50192         this.footer.add(
50193             {
50194                 text : '&lt;',
50195                 xtype: 'Button',
50196                 handler : function() {
50197                     _t.footDisp.scrollTo('left',0,true)
50198                 }
50199             }
50200         );
50201         this.footer.add( footDisp );
50202         this.footer.add( 
50203             {
50204                 text : '&gt;',
50205                 xtype: 'Button',
50206                 handler : function() {
50207                     // no animation..
50208                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50209                 }
50210             }
50211         );
50212         var fel = Roo.get(footDisp.el);
50213         fel.addClass('x-editor-context');
50214         this.footDispWrap = fel; 
50215         this.footDispWrap.overflow  = 'hidden';
50216         
50217         this.footDisp = fel.createChild();
50218         this.footDispWrap.on('click', this.onContextClick, this)
50219         
50220         
50221     },
50222     // when the footer contect changes
50223     onContextClick : function (ev,dom)
50224     {
50225         ev.preventDefault();
50226         var  cn = dom.className;
50227         //Roo.log(cn);
50228         if (!cn.match(/x-ed-loc-/)) {
50229             return;
50230         }
50231         var n = cn.split('-').pop();
50232         var ans = this.footerEls;
50233         var sel = ans[n];
50234         
50235         this.editorcore.selectNode(sel);
50236         
50237         
50238         this.updateToolbar(null, null, sel);
50239         
50240         
50241     }
50242     
50243     
50244     
50245     
50246     
50247 });
50248
50249
50250
50251
50252
50253 /*
50254  * Based on:
50255  * Ext JS Library 1.1.1
50256  * Copyright(c) 2006-2007, Ext JS, LLC.
50257  *
50258  * Originally Released Under LGPL - original licence link has changed is not relivant.
50259  *
50260  * Fork - LGPL
50261  * <script type="text/javascript">
50262  */
50263  
50264 /**
50265  * @class Roo.form.BasicForm
50266  * @extends Roo.util.Observable
50267  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50268  * @constructor
50269  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50270  * @param {Object} config Configuration options
50271  */
50272 Roo.form.BasicForm = function(el, config){
50273     this.allItems = [];
50274     this.childForms = [];
50275     Roo.apply(this, config);
50276     /*
50277      * The Roo.form.Field items in this form.
50278      * @type MixedCollection
50279      */
50280      
50281      
50282     this.items = new Roo.util.MixedCollection(false, function(o){
50283         return o.id || (o.id = Roo.id());
50284     });
50285     this.addEvents({
50286         /**
50287          * @event beforeaction
50288          * Fires before any action is performed. Return false to cancel the action.
50289          * @param {Form} this
50290          * @param {Action} action The action to be performed
50291          */
50292         beforeaction: true,
50293         /**
50294          * @event actionfailed
50295          * Fires when an action fails.
50296          * @param {Form} this
50297          * @param {Action} action The action that failed
50298          */
50299         actionfailed : true,
50300         /**
50301          * @event actioncomplete
50302          * Fires when an action is completed.
50303          * @param {Form} this
50304          * @param {Action} action The action that completed
50305          */
50306         actioncomplete : true
50307     });
50308     if(el){
50309         this.initEl(el);
50310     }
50311     Roo.form.BasicForm.superclass.constructor.call(this);
50312     
50313     Roo.form.BasicForm.popover.apply();
50314 };
50315
50316 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50317     /**
50318      * @cfg {String} method
50319      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50320      */
50321     /**
50322      * @cfg {DataReader} reader
50323      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50324      * This is optional as there is built-in support for processing JSON.
50325      */
50326     /**
50327      * @cfg {DataReader} errorReader
50328      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50329      * This is completely optional as there is built-in support for processing JSON.
50330      */
50331     /**
50332      * @cfg {String} url
50333      * The URL to use for form actions if one isn't supplied in the action options.
50334      */
50335     /**
50336      * @cfg {Boolean} fileUpload
50337      * Set to true if this form is a file upload.
50338      */
50339      
50340     /**
50341      * @cfg {Object} baseParams
50342      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50343      */
50344      /**
50345      
50346     /**
50347      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50348      */
50349     timeout: 30,
50350
50351     // private
50352     activeAction : null,
50353
50354     /**
50355      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50356      * or setValues() data instead of when the form was first created.
50357      */
50358     trackResetOnLoad : false,
50359     
50360     
50361     /**
50362      * childForms - used for multi-tab forms
50363      * @type {Array}
50364      */
50365     childForms : false,
50366     
50367     /**
50368      * allItems - full list of fields.
50369      * @type {Array}
50370      */
50371     allItems : false,
50372     
50373     /**
50374      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50375      * element by passing it or its id or mask the form itself by passing in true.
50376      * @type Mixed
50377      */
50378     waitMsgTarget : false,
50379     
50380     /**
50381      * @type Boolean
50382      */
50383     disableMask : false,
50384     
50385     /**
50386      * @cfg {Boolean} errorMask (true|false) default false
50387      */
50388     errorMask : false,
50389     
50390     /**
50391      * @cfg {Number} maskOffset Default 100
50392      */
50393     maskOffset : 100,
50394
50395     // private
50396     initEl : function(el){
50397         this.el = Roo.get(el);
50398         this.id = this.el.id || Roo.id();
50399         this.el.on('submit', this.onSubmit, this);
50400         this.el.addClass('x-form');
50401     },
50402
50403     // private
50404     onSubmit : function(e){
50405         e.stopEvent();
50406     },
50407
50408     /**
50409      * Returns true if client-side validation on the form is successful.
50410      * @return Boolean
50411      */
50412     isValid : function(){
50413         var valid = true;
50414         var target = false;
50415         this.items.each(function(f){
50416             if(f.validate()){
50417                 return;
50418             }
50419             
50420             valid = false;
50421                 
50422             if(!target && f.el.isVisible(true)){
50423                 target = f;
50424             }
50425         });
50426         
50427         if(this.errorMask && !valid){
50428             Roo.form.BasicForm.popover.mask(this, target);
50429         }
50430         
50431         return valid;
50432     },
50433     /**
50434      * Returns array of invalid form fields.
50435      * @return Array
50436      */
50437     
50438     invalidFields : function()
50439     {
50440         var ret = [];
50441         this.items.each(function(f){
50442             if(f.validate()){
50443                 return;
50444             }
50445             ret.push(f);
50446             
50447         });
50448         
50449         return ret;
50450     },
50451     
50452     
50453     /**
50454      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50455      * @return Boolean
50456      */
50457     isDirty : function(){
50458         var dirty = false;
50459         this.items.each(function(f){
50460            if(f.isDirty()){
50461                dirty = true;
50462                return false;
50463            }
50464         });
50465         return dirty;
50466     },
50467     
50468     /**
50469      * Returns true if any fields in this form have changed since their original load. (New version)
50470      * @return Boolean
50471      */
50472     
50473     hasChanged : function()
50474     {
50475         var dirty = false;
50476         this.items.each(function(f){
50477            if(f.hasChanged()){
50478                dirty = true;
50479                return false;
50480            }
50481         });
50482         return dirty;
50483         
50484     },
50485     /**
50486      * Resets all hasChanged to 'false' -
50487      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50488      * So hasChanged storage is only to be used for this purpose
50489      * @return Boolean
50490      */
50491     resetHasChanged : function()
50492     {
50493         this.items.each(function(f){
50494            f.resetHasChanged();
50495         });
50496         
50497     },
50498     
50499     
50500     /**
50501      * Performs a predefined action (submit or load) or custom actions you define on this form.
50502      * @param {String} actionName The name of the action type
50503      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50504      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50505      * accept other config options):
50506      * <pre>
50507 Property          Type             Description
50508 ----------------  ---------------  ----------------------------------------------------------------------------------
50509 url               String           The url for the action (defaults to the form's url)
50510 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50511 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50512 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50513                                    validate the form on the client (defaults to false)
50514      * </pre>
50515      * @return {BasicForm} this
50516      */
50517     doAction : function(action, options){
50518         if(typeof action == 'string'){
50519             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50520         }
50521         if(this.fireEvent('beforeaction', this, action) !== false){
50522             this.beforeAction(action);
50523             action.run.defer(100, action);
50524         }
50525         return this;
50526     },
50527
50528     /**
50529      * Shortcut to do a submit action.
50530      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50531      * @return {BasicForm} this
50532      */
50533     submit : function(options){
50534         this.doAction('submit', options);
50535         return this;
50536     },
50537
50538     /**
50539      * Shortcut to do a load action.
50540      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50541      * @return {BasicForm} this
50542      */
50543     load : function(options){
50544         this.doAction('load', options);
50545         return this;
50546     },
50547
50548     /**
50549      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50550      * @param {Record} record The record to edit
50551      * @return {BasicForm} this
50552      */
50553     updateRecord : function(record){
50554         record.beginEdit();
50555         var fs = record.fields;
50556         fs.each(function(f){
50557             var field = this.findField(f.name);
50558             if(field){
50559                 record.set(f.name, field.getValue());
50560             }
50561         }, this);
50562         record.endEdit();
50563         return this;
50564     },
50565
50566     /**
50567      * Loads an Roo.data.Record into this form.
50568      * @param {Record} record The record to load
50569      * @return {BasicForm} this
50570      */
50571     loadRecord : function(record){
50572         this.setValues(record.data);
50573         return this;
50574     },
50575
50576     // private
50577     beforeAction : function(action){
50578         var o = action.options;
50579         
50580         if(!this.disableMask) {
50581             if(this.waitMsgTarget === true){
50582                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50583             }else if(this.waitMsgTarget){
50584                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50585                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50586             }else {
50587                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50588             }
50589         }
50590         
50591          
50592     },
50593
50594     // private
50595     afterAction : function(action, success){
50596         this.activeAction = null;
50597         var o = action.options;
50598         
50599         if(!this.disableMask) {
50600             if(this.waitMsgTarget === true){
50601                 this.el.unmask();
50602             }else if(this.waitMsgTarget){
50603                 this.waitMsgTarget.unmask();
50604             }else{
50605                 Roo.MessageBox.updateProgress(1);
50606                 Roo.MessageBox.hide();
50607             }
50608         }
50609         
50610         if(success){
50611             if(o.reset){
50612                 this.reset();
50613             }
50614             Roo.callback(o.success, o.scope, [this, action]);
50615             this.fireEvent('actioncomplete', this, action);
50616             
50617         }else{
50618             
50619             // failure condition..
50620             // we have a scenario where updates need confirming.
50621             // eg. if a locking scenario exists..
50622             // we look for { errors : { needs_confirm : true }} in the response.
50623             if (
50624                 (typeof(action.result) != 'undefined')  &&
50625                 (typeof(action.result.errors) != 'undefined')  &&
50626                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50627            ){
50628                 var _t = this;
50629                 Roo.MessageBox.confirm(
50630                     "Change requires confirmation",
50631                     action.result.errorMsg,
50632                     function(r) {
50633                         if (r != 'yes') {
50634                             return;
50635                         }
50636                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50637                     }
50638                     
50639                 );
50640                 
50641                 
50642                 
50643                 return;
50644             }
50645             
50646             Roo.callback(o.failure, o.scope, [this, action]);
50647             // show an error message if no failed handler is set..
50648             if (!this.hasListener('actionfailed')) {
50649                 Roo.MessageBox.alert("Error",
50650                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50651                         action.result.errorMsg :
50652                         "Saving Failed, please check your entries or try again"
50653                 );
50654             }
50655             
50656             this.fireEvent('actionfailed', this, action);
50657         }
50658         
50659     },
50660
50661     /**
50662      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50663      * @param {String} id The value to search for
50664      * @return Field
50665      */
50666     findField : function(id){
50667         var field = this.items.get(id);
50668         if(!field){
50669             this.items.each(function(f){
50670                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50671                     field = f;
50672                     return false;
50673                 }
50674             });
50675         }
50676         return field || null;
50677     },
50678
50679     /**
50680      * Add a secondary form to this one, 
50681      * Used to provide tabbed forms. One form is primary, with hidden values 
50682      * which mirror the elements from the other forms.
50683      * 
50684      * @param {Roo.form.Form} form to add.
50685      * 
50686      */
50687     addForm : function(form)
50688     {
50689        
50690         if (this.childForms.indexOf(form) > -1) {
50691             // already added..
50692             return;
50693         }
50694         this.childForms.push(form);
50695         var n = '';
50696         Roo.each(form.allItems, function (fe) {
50697             
50698             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50699             if (this.findField(n)) { // already added..
50700                 return;
50701             }
50702             var add = new Roo.form.Hidden({
50703                 name : n
50704             });
50705             add.render(this.el);
50706             
50707             this.add( add );
50708         }, this);
50709         
50710     },
50711     /**
50712      * Mark fields in this form invalid in bulk.
50713      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50714      * @return {BasicForm} this
50715      */
50716     markInvalid : function(errors){
50717         if(errors instanceof Array){
50718             for(var i = 0, len = errors.length; i < len; i++){
50719                 var fieldError = errors[i];
50720                 var f = this.findField(fieldError.id);
50721                 if(f){
50722                     f.markInvalid(fieldError.msg);
50723                 }
50724             }
50725         }else{
50726             var field, id;
50727             for(id in errors){
50728                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50729                     field.markInvalid(errors[id]);
50730                 }
50731             }
50732         }
50733         Roo.each(this.childForms || [], function (f) {
50734             f.markInvalid(errors);
50735         });
50736         
50737         return this;
50738     },
50739
50740     /**
50741      * Set values for fields in this form in bulk.
50742      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50743      * @return {BasicForm} this
50744      */
50745     setValues : function(values){
50746         if(values instanceof Array){ // array of objects
50747             for(var i = 0, len = values.length; i < len; i++){
50748                 var v = values[i];
50749                 var f = this.findField(v.id);
50750                 if(f){
50751                     f.setValue(v.value);
50752                     if(this.trackResetOnLoad){
50753                         f.originalValue = f.getValue();
50754                     }
50755                 }
50756             }
50757         }else{ // object hash
50758             var field, id;
50759             for(id in values){
50760                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50761                     
50762                     if (field.setFromData && 
50763                         field.valueField && 
50764                         field.displayField &&
50765                         // combos' with local stores can 
50766                         // be queried via setValue()
50767                         // to set their value..
50768                         (field.store && !field.store.isLocal)
50769                         ) {
50770                         // it's a combo
50771                         var sd = { };
50772                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50773                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50774                         field.setFromData(sd);
50775                         
50776                     } else {
50777                         field.setValue(values[id]);
50778                     }
50779                     
50780                     
50781                     if(this.trackResetOnLoad){
50782                         field.originalValue = field.getValue();
50783                     }
50784                 }
50785             }
50786         }
50787         this.resetHasChanged();
50788         
50789         
50790         Roo.each(this.childForms || [], function (f) {
50791             f.setValues(values);
50792             f.resetHasChanged();
50793         });
50794                 
50795         return this;
50796     },
50797  
50798     /**
50799      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50800      * they are returned as an array.
50801      * @param {Boolean} asString
50802      * @return {Object}
50803      */
50804     getValues : function(asString)
50805     {
50806         if (this.childForms) {
50807             // copy values from the child forms
50808             Roo.each(this.childForms, function (f) {
50809                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50810             }, this);
50811         }
50812         
50813         // use formdata
50814         if (typeof(FormData) != 'undefined' && asString !== true) {
50815             // this relies on a 'recent' version of chrome apparently...
50816             try {
50817                 var fd = (new FormData(this.el.dom)).entries();
50818                 var ret = {};
50819                 var ent = fd.next();
50820                 while (!ent.done) {
50821                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50822                     ent = fd.next();
50823                 };
50824                 return ret;
50825             } catch(e) {
50826                 
50827             }
50828             
50829         }
50830         
50831         
50832         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50833         if(asString === true){
50834             return fs;
50835         }
50836         return Roo.urlDecode(fs);
50837     },
50838     
50839     /**
50840      * Returns the fields in this form as an object with key/value pairs. 
50841      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50842      * Normally this will not return readOnly data 
50843      * @param {Boolean} with_readonly return readonly field data.
50844      * @return {Object}
50845      */
50846     getFieldValues : function(with_readonly)
50847     {
50848         if (this.childForms) {
50849             // copy values from the child forms
50850             // should this call getFieldValues - probably not as we do not currently copy
50851             // hidden fields when we generate..
50852             Roo.each(this.childForms, function (f) {
50853                 this.setValues(f.getFieldValues());
50854             }, this);
50855         }
50856         
50857         var ret = {};
50858         this.items.each(function(f){
50859             
50860             if (f.readOnly && with_readonly !== true) {
50861                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50862                         // if a subform contains a copy of them.
50863                         // if you have subforms with the same editable data, you will need to copy the data back
50864                         // and forth.
50865             }
50866             
50867             if (!f.getName()) {
50868                 return;
50869             }
50870             var v = f.getValue();
50871             if (f.inputType =='radio') {
50872                 if (typeof(ret[f.getName()]) == 'undefined') {
50873                     ret[f.getName()] = ''; // empty..
50874                 }
50875                 
50876                 if (!f.el.dom.checked) {
50877                     return;
50878                     
50879                 }
50880                 v = f.el.dom.value;
50881                 
50882             }
50883             
50884             // not sure if this supported any more..
50885             if ((typeof(v) == 'object') && f.getRawValue) {
50886                 v = f.getRawValue() ; // dates..
50887             }
50888             // combo boxes where name != hiddenName...
50889             if (f.name != f.getName()) {
50890                 ret[f.name] = f.getRawValue();
50891             }
50892             ret[f.getName()] = v;
50893         });
50894         
50895         return ret;
50896     },
50897
50898     /**
50899      * Clears all invalid messages in this form.
50900      * @return {BasicForm} this
50901      */
50902     clearInvalid : function(){
50903         this.items.each(function(f){
50904            f.clearInvalid();
50905         });
50906         
50907         Roo.each(this.childForms || [], function (f) {
50908             f.clearInvalid();
50909         });
50910         
50911         
50912         return this;
50913     },
50914
50915     /**
50916      * Resets this form.
50917      * @return {BasicForm} this
50918      */
50919     reset : function(){
50920         this.items.each(function(f){
50921             f.reset();
50922         });
50923         
50924         Roo.each(this.childForms || [], function (f) {
50925             f.reset();
50926         });
50927         this.resetHasChanged();
50928         
50929         return this;
50930     },
50931
50932     /**
50933      * Add Roo.form components to this form.
50934      * @param {Field} field1
50935      * @param {Field} field2 (optional)
50936      * @param {Field} etc (optional)
50937      * @return {BasicForm} this
50938      */
50939     add : function(){
50940         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50941         return this;
50942     },
50943
50944
50945     /**
50946      * Removes a field from the items collection (does NOT remove its markup).
50947      * @param {Field} field
50948      * @return {BasicForm} this
50949      */
50950     remove : function(field){
50951         this.items.remove(field);
50952         return this;
50953     },
50954
50955     /**
50956      * Looks at the fields in this form, checks them for an id attribute,
50957      * and calls applyTo on the existing dom element with that id.
50958      * @return {BasicForm} this
50959      */
50960     render : function(){
50961         this.items.each(function(f){
50962             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50963                 f.applyTo(f.id);
50964             }
50965         });
50966         return this;
50967     },
50968
50969     /**
50970      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50971      * @param {Object} values
50972      * @return {BasicForm} this
50973      */
50974     applyToFields : function(o){
50975         this.items.each(function(f){
50976            Roo.apply(f, o);
50977         });
50978         return this;
50979     },
50980
50981     /**
50982      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50983      * @param {Object} values
50984      * @return {BasicForm} this
50985      */
50986     applyIfToFields : function(o){
50987         this.items.each(function(f){
50988            Roo.applyIf(f, o);
50989         });
50990         return this;
50991     }
50992 });
50993
50994 // back compat
50995 Roo.BasicForm = Roo.form.BasicForm;
50996
50997 Roo.apply(Roo.form.BasicForm, {
50998     
50999     popover : {
51000         
51001         padding : 5,
51002         
51003         isApplied : false,
51004         
51005         isMasked : false,
51006         
51007         form : false,
51008         
51009         target : false,
51010         
51011         intervalID : false,
51012         
51013         maskEl : false,
51014         
51015         apply : function()
51016         {
51017             if(this.isApplied){
51018                 return;
51019             }
51020             
51021             this.maskEl = {
51022                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51023                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51024                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51025                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51026             };
51027             
51028             this.maskEl.top.enableDisplayMode("block");
51029             this.maskEl.left.enableDisplayMode("block");
51030             this.maskEl.bottom.enableDisplayMode("block");
51031             this.maskEl.right.enableDisplayMode("block");
51032             
51033             Roo.get(document.body).on('click', function(){
51034                 this.unmask();
51035             }, this);
51036             
51037             Roo.get(document.body).on('touchstart', function(){
51038                 this.unmask();
51039             }, this);
51040             
51041             this.isApplied = true
51042         },
51043         
51044         mask : function(form, target)
51045         {
51046             this.form = form;
51047             
51048             this.target = target;
51049             
51050             if(!this.form.errorMask || !target.el){
51051                 return;
51052             }
51053             
51054             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51055             
51056             var ot = this.target.el.calcOffsetsTo(scrollable);
51057             
51058             var scrollTo = ot[1] - this.form.maskOffset;
51059             
51060             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51061             
51062             scrollable.scrollTo('top', scrollTo);
51063             
51064             var el = this.target.wrap || this.target.el;
51065             
51066             var box = el.getBox();
51067             
51068             this.maskEl.top.setStyle('position', 'absolute');
51069             this.maskEl.top.setStyle('z-index', 10000);
51070             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51071             this.maskEl.top.setLeft(0);
51072             this.maskEl.top.setTop(0);
51073             this.maskEl.top.show();
51074             
51075             this.maskEl.left.setStyle('position', 'absolute');
51076             this.maskEl.left.setStyle('z-index', 10000);
51077             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51078             this.maskEl.left.setLeft(0);
51079             this.maskEl.left.setTop(box.y - this.padding);
51080             this.maskEl.left.show();
51081
51082             this.maskEl.bottom.setStyle('position', 'absolute');
51083             this.maskEl.bottom.setStyle('z-index', 10000);
51084             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51085             this.maskEl.bottom.setLeft(0);
51086             this.maskEl.bottom.setTop(box.bottom + this.padding);
51087             this.maskEl.bottom.show();
51088
51089             this.maskEl.right.setStyle('position', 'absolute');
51090             this.maskEl.right.setStyle('z-index', 10000);
51091             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51092             this.maskEl.right.setLeft(box.right + this.padding);
51093             this.maskEl.right.setTop(box.y - this.padding);
51094             this.maskEl.right.show();
51095
51096             this.intervalID = window.setInterval(function() {
51097                 Roo.form.BasicForm.popover.unmask();
51098             }, 10000);
51099
51100             window.onwheel = function(){ return false;};
51101             
51102             (function(){ this.isMasked = true; }).defer(500, this);
51103             
51104         },
51105         
51106         unmask : function()
51107         {
51108             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51109                 return;
51110             }
51111             
51112             this.maskEl.top.setStyle('position', 'absolute');
51113             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51114             this.maskEl.top.hide();
51115
51116             this.maskEl.left.setStyle('position', 'absolute');
51117             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51118             this.maskEl.left.hide();
51119
51120             this.maskEl.bottom.setStyle('position', 'absolute');
51121             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51122             this.maskEl.bottom.hide();
51123
51124             this.maskEl.right.setStyle('position', 'absolute');
51125             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51126             this.maskEl.right.hide();
51127             
51128             window.onwheel = function(){ return true;};
51129             
51130             if(this.intervalID){
51131                 window.clearInterval(this.intervalID);
51132                 this.intervalID = false;
51133             }
51134             
51135             this.isMasked = false;
51136             
51137         }
51138         
51139     }
51140     
51141 });/*
51142  * Based on:
51143  * Ext JS Library 1.1.1
51144  * Copyright(c) 2006-2007, Ext JS, LLC.
51145  *
51146  * Originally Released Under LGPL - original licence link has changed is not relivant.
51147  *
51148  * Fork - LGPL
51149  * <script type="text/javascript">
51150  */
51151
51152 /**
51153  * @class Roo.form.Form
51154  * @extends Roo.form.BasicForm
51155  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51156  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51157  * @constructor
51158  * @param {Object} config Configuration options
51159  */
51160 Roo.form.Form = function(config){
51161     var xitems =  [];
51162     if (config.items) {
51163         xitems = config.items;
51164         delete config.items;
51165     }
51166    
51167     
51168     Roo.form.Form.superclass.constructor.call(this, null, config);
51169     this.url = this.url || this.action;
51170     if(!this.root){
51171         this.root = new Roo.form.Layout(Roo.applyIf({
51172             id: Roo.id()
51173         }, config));
51174     }
51175     this.active = this.root;
51176     /**
51177      * Array of all the buttons that have been added to this form via {@link addButton}
51178      * @type Array
51179      */
51180     this.buttons = [];
51181     this.allItems = [];
51182     this.addEvents({
51183         /**
51184          * @event clientvalidation
51185          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51186          * @param {Form} this
51187          * @param {Boolean} valid true if the form has passed client-side validation
51188          */
51189         clientvalidation: true,
51190         /**
51191          * @event rendered
51192          * Fires when the form is rendered
51193          * @param {Roo.form.Form} form
51194          */
51195         rendered : true
51196     });
51197     
51198     if (this.progressUrl) {
51199             // push a hidden field onto the list of fields..
51200             this.addxtype( {
51201                     xns: Roo.form, 
51202                     xtype : 'Hidden', 
51203                     name : 'UPLOAD_IDENTIFIER' 
51204             });
51205         }
51206         
51207     
51208     Roo.each(xitems, this.addxtype, this);
51209     
51210 };
51211
51212 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51213      /**
51214      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51215      */
51216     
51217     /**
51218      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51219      */
51220     /**
51221      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51222      */
51223     /**
51224      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51225      */
51226     buttonAlign:'center',
51227
51228     /**
51229      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51230      */
51231     minButtonWidth:75,
51232
51233     /**
51234      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51235      * This property cascades to child containers if not set.
51236      */
51237     labelAlign:'left',
51238
51239     /**
51240      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51241      * fires a looping event with that state. This is required to bind buttons to the valid
51242      * state using the config value formBind:true on the button.
51243      */
51244     monitorValid : false,
51245
51246     /**
51247      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51248      */
51249     monitorPoll : 200,
51250     
51251     /**
51252      * @cfg {String} progressUrl - Url to return progress data 
51253      */
51254     
51255     progressUrl : false,
51256     /**
51257      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51258      * sending a formdata with extra parameters - eg uploaded elements.
51259      */
51260     
51261     formData : false,
51262     
51263     /**
51264      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51265      * fields are added and the column is closed. If no fields are passed the column remains open
51266      * until end() is called.
51267      * @param {Object} config The config to pass to the column
51268      * @param {Field} field1 (optional)
51269      * @param {Field} field2 (optional)
51270      * @param {Field} etc (optional)
51271      * @return Column The column container object
51272      */
51273     column : function(c){
51274         var col = new Roo.form.Column(c);
51275         this.start(col);
51276         if(arguments.length > 1){ // duplicate code required because of Opera
51277             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51278             this.end();
51279         }
51280         return col;
51281     },
51282
51283     /**
51284      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51285      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51286      * until end() is called.
51287      * @param {Object} config The config to pass to the fieldset
51288      * @param {Field} field1 (optional)
51289      * @param {Field} field2 (optional)
51290      * @param {Field} etc (optional)
51291      * @return FieldSet The fieldset container object
51292      */
51293     fieldset : function(c){
51294         var fs = new Roo.form.FieldSet(c);
51295         this.start(fs);
51296         if(arguments.length > 1){ // duplicate code required because of Opera
51297             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51298             this.end();
51299         }
51300         return fs;
51301     },
51302
51303     /**
51304      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51305      * fields are added and the container is closed. If no fields are passed the container remains open
51306      * until end() is called.
51307      * @param {Object} config The config to pass to the Layout
51308      * @param {Field} field1 (optional)
51309      * @param {Field} field2 (optional)
51310      * @param {Field} etc (optional)
51311      * @return Layout The container object
51312      */
51313     container : function(c){
51314         var l = new Roo.form.Layout(c);
51315         this.start(l);
51316         if(arguments.length > 1){ // duplicate code required because of Opera
51317             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51318             this.end();
51319         }
51320         return l;
51321     },
51322
51323     /**
51324      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51325      * @param {Object} container A Roo.form.Layout or subclass of Layout
51326      * @return {Form} this
51327      */
51328     start : function(c){
51329         // cascade label info
51330         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51331         this.active.stack.push(c);
51332         c.ownerCt = this.active;
51333         this.active = c;
51334         return this;
51335     },
51336
51337     /**
51338      * Closes the current open container
51339      * @return {Form} this
51340      */
51341     end : function(){
51342         if(this.active == this.root){
51343             return this;
51344         }
51345         this.active = this.active.ownerCt;
51346         return this;
51347     },
51348
51349     /**
51350      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51351      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51352      * as the label of the field.
51353      * @param {Field} field1
51354      * @param {Field} field2 (optional)
51355      * @param {Field} etc. (optional)
51356      * @return {Form} this
51357      */
51358     add : function(){
51359         this.active.stack.push.apply(this.active.stack, arguments);
51360         this.allItems.push.apply(this.allItems,arguments);
51361         var r = [];
51362         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51363             if(a[i].isFormField){
51364                 r.push(a[i]);
51365             }
51366         }
51367         if(r.length > 0){
51368             Roo.form.Form.superclass.add.apply(this, r);
51369         }
51370         return this;
51371     },
51372     
51373
51374     
51375     
51376     
51377      /**
51378      * Find any element that has been added to a form, using it's ID or name
51379      * This can include framesets, columns etc. along with regular fields..
51380      * @param {String} id - id or name to find.
51381      
51382      * @return {Element} e - or false if nothing found.
51383      */
51384     findbyId : function(id)
51385     {
51386         var ret = false;
51387         if (!id) {
51388             return ret;
51389         }
51390         Roo.each(this.allItems, function(f){
51391             if (f.id == id || f.name == id ){
51392                 ret = f;
51393                 return false;
51394             }
51395         });
51396         return ret;
51397     },
51398
51399     
51400     
51401     /**
51402      * Render this form into the passed container. This should only be called once!
51403      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51404      * @return {Form} this
51405      */
51406     render : function(ct)
51407     {
51408         
51409         
51410         
51411         ct = Roo.get(ct);
51412         var o = this.autoCreate || {
51413             tag: 'form',
51414             method : this.method || 'POST',
51415             id : this.id || Roo.id()
51416         };
51417         this.initEl(ct.createChild(o));
51418
51419         this.root.render(this.el);
51420         
51421        
51422              
51423         this.items.each(function(f){
51424             f.render('x-form-el-'+f.id);
51425         });
51426
51427         if(this.buttons.length > 0){
51428             // tables are required to maintain order and for correct IE layout
51429             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51430                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51431                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51432             }}, null, true);
51433             var tr = tb.getElementsByTagName('tr')[0];
51434             for(var i = 0, len = this.buttons.length; i < len; i++) {
51435                 var b = this.buttons[i];
51436                 var td = document.createElement('td');
51437                 td.className = 'x-form-btn-td';
51438                 b.render(tr.appendChild(td));
51439             }
51440         }
51441         if(this.monitorValid){ // initialize after render
51442             this.startMonitoring();
51443         }
51444         this.fireEvent('rendered', this);
51445         return this;
51446     },
51447
51448     /**
51449      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51450      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51451      * object or a valid Roo.DomHelper element config
51452      * @param {Function} handler The function called when the button is clicked
51453      * @param {Object} scope (optional) The scope of the handler function
51454      * @return {Roo.Button}
51455      */
51456     addButton : function(config, handler, scope){
51457         var bc = {
51458             handler: handler,
51459             scope: scope,
51460             minWidth: this.minButtonWidth,
51461             hideParent:true
51462         };
51463         if(typeof config == "string"){
51464             bc.text = config;
51465         }else{
51466             Roo.apply(bc, config);
51467         }
51468         var btn = new Roo.Button(null, bc);
51469         this.buttons.push(btn);
51470         return btn;
51471     },
51472
51473      /**
51474      * Adds a series of form elements (using the xtype property as the factory method.
51475      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51476      * @param {Object} config 
51477      */
51478     
51479     addxtype : function()
51480     {
51481         var ar = Array.prototype.slice.call(arguments, 0);
51482         var ret = false;
51483         for(var i = 0; i < ar.length; i++) {
51484             if (!ar[i]) {
51485                 continue; // skip -- if this happends something invalid got sent, we 
51486                 // should ignore it, as basically that interface element will not show up
51487                 // and that should be pretty obvious!!
51488             }
51489             
51490             if (Roo.form[ar[i].xtype]) {
51491                 ar[i].form = this;
51492                 var fe = Roo.factory(ar[i], Roo.form);
51493                 if (!ret) {
51494                     ret = fe;
51495                 }
51496                 fe.form = this;
51497                 if (fe.store) {
51498                     fe.store.form = this;
51499                 }
51500                 if (fe.isLayout) {  
51501                          
51502                     this.start(fe);
51503                     this.allItems.push(fe);
51504                     if (fe.items && fe.addxtype) {
51505                         fe.addxtype.apply(fe, fe.items);
51506                         delete fe.items;
51507                     }
51508                      this.end();
51509                     continue;
51510                 }
51511                 
51512                 
51513                  
51514                 this.add(fe);
51515               //  console.log('adding ' + ar[i].xtype);
51516             }
51517             if (ar[i].xtype == 'Button') {  
51518                 //console.log('adding button');
51519                 //console.log(ar[i]);
51520                 this.addButton(ar[i]);
51521                 this.allItems.push(fe);
51522                 continue;
51523             }
51524             
51525             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51526                 alert('end is not supported on xtype any more, use items');
51527             //    this.end();
51528             //    //console.log('adding end');
51529             }
51530             
51531         }
51532         return ret;
51533     },
51534     
51535     /**
51536      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51537      * option "monitorValid"
51538      */
51539     startMonitoring : function(){
51540         if(!this.bound){
51541             this.bound = true;
51542             Roo.TaskMgr.start({
51543                 run : this.bindHandler,
51544                 interval : this.monitorPoll || 200,
51545                 scope: this
51546             });
51547         }
51548     },
51549
51550     /**
51551      * Stops monitoring of the valid state of this form
51552      */
51553     stopMonitoring : function(){
51554         this.bound = false;
51555     },
51556
51557     // private
51558     bindHandler : function(){
51559         if(!this.bound){
51560             return false; // stops binding
51561         }
51562         var valid = true;
51563         this.items.each(function(f){
51564             if(!f.isValid(true)){
51565                 valid = false;
51566                 return false;
51567             }
51568         });
51569         for(var i = 0, len = this.buttons.length; i < len; i++){
51570             var btn = this.buttons[i];
51571             if(btn.formBind === true && btn.disabled === valid){
51572                 btn.setDisabled(!valid);
51573             }
51574         }
51575         this.fireEvent('clientvalidation', this, valid);
51576     }
51577     
51578     
51579     
51580     
51581     
51582     
51583     
51584     
51585 });
51586
51587
51588 // back compat
51589 Roo.Form = Roo.form.Form;
51590 /*
51591  * Based on:
51592  * Ext JS Library 1.1.1
51593  * Copyright(c) 2006-2007, Ext JS, LLC.
51594  *
51595  * Originally Released Under LGPL - original licence link has changed is not relivant.
51596  *
51597  * Fork - LGPL
51598  * <script type="text/javascript">
51599  */
51600
51601 // as we use this in bootstrap.
51602 Roo.namespace('Roo.form');
51603  /**
51604  * @class Roo.form.Action
51605  * Internal Class used to handle form actions
51606  * @constructor
51607  * @param {Roo.form.BasicForm} el The form element or its id
51608  * @param {Object} config Configuration options
51609  */
51610
51611  
51612  
51613 // define the action interface
51614 Roo.form.Action = function(form, options){
51615     this.form = form;
51616     this.options = options || {};
51617 };
51618 /**
51619  * Client Validation Failed
51620  * @const 
51621  */
51622 Roo.form.Action.CLIENT_INVALID = 'client';
51623 /**
51624  * Server Validation Failed
51625  * @const 
51626  */
51627 Roo.form.Action.SERVER_INVALID = 'server';
51628  /**
51629  * Connect to Server Failed
51630  * @const 
51631  */
51632 Roo.form.Action.CONNECT_FAILURE = 'connect';
51633 /**
51634  * Reading Data from Server Failed
51635  * @const 
51636  */
51637 Roo.form.Action.LOAD_FAILURE = 'load';
51638
51639 Roo.form.Action.prototype = {
51640     type : 'default',
51641     failureType : undefined,
51642     response : undefined,
51643     result : undefined,
51644
51645     // interface method
51646     run : function(options){
51647
51648     },
51649
51650     // interface method
51651     success : function(response){
51652
51653     },
51654
51655     // interface method
51656     handleResponse : function(response){
51657
51658     },
51659
51660     // default connection failure
51661     failure : function(response){
51662         
51663         this.response = response;
51664         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51665         this.form.afterAction(this, false);
51666     },
51667
51668     processResponse : function(response){
51669         this.response = response;
51670         if(!response.responseText){
51671             return true;
51672         }
51673         this.result = this.handleResponse(response);
51674         return this.result;
51675     },
51676
51677     // utility functions used internally
51678     getUrl : function(appendParams){
51679         var url = this.options.url || this.form.url || this.form.el.dom.action;
51680         if(appendParams){
51681             var p = this.getParams();
51682             if(p){
51683                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51684             }
51685         }
51686         return url;
51687     },
51688
51689     getMethod : function(){
51690         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51691     },
51692
51693     getParams : function(){
51694         var bp = this.form.baseParams;
51695         var p = this.options.params;
51696         if(p){
51697             if(typeof p == "object"){
51698                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51699             }else if(typeof p == 'string' && bp){
51700                 p += '&' + Roo.urlEncode(bp);
51701             }
51702         }else if(bp){
51703             p = Roo.urlEncode(bp);
51704         }
51705         return p;
51706     },
51707
51708     createCallback : function(){
51709         return {
51710             success: this.success,
51711             failure: this.failure,
51712             scope: this,
51713             timeout: (this.form.timeout*1000),
51714             upload: this.form.fileUpload ? this.success : undefined
51715         };
51716     }
51717 };
51718
51719 Roo.form.Action.Submit = function(form, options){
51720     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51721 };
51722
51723 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51724     type : 'submit',
51725
51726     haveProgress : false,
51727     uploadComplete : false,
51728     
51729     // uploadProgress indicator.
51730     uploadProgress : function()
51731     {
51732         if (!this.form.progressUrl) {
51733             return;
51734         }
51735         
51736         if (!this.haveProgress) {
51737             Roo.MessageBox.progress("Uploading", "Uploading");
51738         }
51739         if (this.uploadComplete) {
51740            Roo.MessageBox.hide();
51741            return;
51742         }
51743         
51744         this.haveProgress = true;
51745    
51746         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51747         
51748         var c = new Roo.data.Connection();
51749         c.request({
51750             url : this.form.progressUrl,
51751             params: {
51752                 id : uid
51753             },
51754             method: 'GET',
51755             success : function(req){
51756                //console.log(data);
51757                 var rdata = false;
51758                 var edata;
51759                 try  {
51760                    rdata = Roo.decode(req.responseText)
51761                 } catch (e) {
51762                     Roo.log("Invalid data from server..");
51763                     Roo.log(edata);
51764                     return;
51765                 }
51766                 if (!rdata || !rdata.success) {
51767                     Roo.log(rdata);
51768                     Roo.MessageBox.alert(Roo.encode(rdata));
51769                     return;
51770                 }
51771                 var data = rdata.data;
51772                 
51773                 if (this.uploadComplete) {
51774                    Roo.MessageBox.hide();
51775                    return;
51776                 }
51777                    
51778                 if (data){
51779                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51780                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51781                     );
51782                 }
51783                 this.uploadProgress.defer(2000,this);
51784             },
51785        
51786             failure: function(data) {
51787                 Roo.log('progress url failed ');
51788                 Roo.log(data);
51789             },
51790             scope : this
51791         });
51792            
51793     },
51794     
51795     
51796     run : function()
51797     {
51798         // run get Values on the form, so it syncs any secondary forms.
51799         this.form.getValues();
51800         
51801         var o = this.options;
51802         var method = this.getMethod();
51803         var isPost = method == 'POST';
51804         if(o.clientValidation === false || this.form.isValid()){
51805             
51806             if (this.form.progressUrl) {
51807                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51808                     (new Date() * 1) + '' + Math.random());
51809                     
51810             } 
51811             
51812             
51813             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51814                 form:this.form.el.dom,
51815                 url:this.getUrl(!isPost),
51816                 method: method,
51817                 params:isPost ? this.getParams() : null,
51818                 isUpload: this.form.fileUpload,
51819                 formData : this.form.formData
51820             }));
51821             
51822             this.uploadProgress();
51823
51824         }else if (o.clientValidation !== false){ // client validation failed
51825             this.failureType = Roo.form.Action.CLIENT_INVALID;
51826             this.form.afterAction(this, false);
51827         }
51828     },
51829
51830     success : function(response)
51831     {
51832         this.uploadComplete= true;
51833         if (this.haveProgress) {
51834             Roo.MessageBox.hide();
51835         }
51836         
51837         
51838         var result = this.processResponse(response);
51839         if(result === true || result.success){
51840             this.form.afterAction(this, true);
51841             return;
51842         }
51843         if(result.errors){
51844             this.form.markInvalid(result.errors);
51845             this.failureType = Roo.form.Action.SERVER_INVALID;
51846         }
51847         this.form.afterAction(this, false);
51848     },
51849     failure : function(response)
51850     {
51851         this.uploadComplete= true;
51852         if (this.haveProgress) {
51853             Roo.MessageBox.hide();
51854         }
51855         
51856         this.response = response;
51857         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51858         this.form.afterAction(this, false);
51859     },
51860     
51861     handleResponse : function(response){
51862         if(this.form.errorReader){
51863             var rs = this.form.errorReader.read(response);
51864             var errors = [];
51865             if(rs.records){
51866                 for(var i = 0, len = rs.records.length; i < len; i++) {
51867                     var r = rs.records[i];
51868                     errors[i] = r.data;
51869                 }
51870             }
51871             if(errors.length < 1){
51872                 errors = null;
51873             }
51874             return {
51875                 success : rs.success,
51876                 errors : errors
51877             };
51878         }
51879         var ret = false;
51880         try {
51881             ret = Roo.decode(response.responseText);
51882         } catch (e) {
51883             ret = {
51884                 success: false,
51885                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51886                 errors : []
51887             };
51888         }
51889         return ret;
51890         
51891     }
51892 });
51893
51894
51895 Roo.form.Action.Load = function(form, options){
51896     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51897     this.reader = this.form.reader;
51898 };
51899
51900 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51901     type : 'load',
51902
51903     run : function(){
51904         
51905         Roo.Ajax.request(Roo.apply(
51906                 this.createCallback(), {
51907                     method:this.getMethod(),
51908                     url:this.getUrl(false),
51909                     params:this.getParams()
51910         }));
51911     },
51912
51913     success : function(response){
51914         
51915         var result = this.processResponse(response);
51916         if(result === true || !result.success || !result.data){
51917             this.failureType = Roo.form.Action.LOAD_FAILURE;
51918             this.form.afterAction(this, false);
51919             return;
51920         }
51921         this.form.clearInvalid();
51922         this.form.setValues(result.data);
51923         this.form.afterAction(this, true);
51924     },
51925
51926     handleResponse : function(response){
51927         if(this.form.reader){
51928             var rs = this.form.reader.read(response);
51929             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51930             return {
51931                 success : rs.success,
51932                 data : data
51933             };
51934         }
51935         return Roo.decode(response.responseText);
51936     }
51937 });
51938
51939 Roo.form.Action.ACTION_TYPES = {
51940     'load' : Roo.form.Action.Load,
51941     'submit' : Roo.form.Action.Submit
51942 };/*
51943  * Based on:
51944  * Ext JS Library 1.1.1
51945  * Copyright(c) 2006-2007, Ext JS, LLC.
51946  *
51947  * Originally Released Under LGPL - original licence link has changed is not relivant.
51948  *
51949  * Fork - LGPL
51950  * <script type="text/javascript">
51951  */
51952  
51953 /**
51954  * @class Roo.form.Layout
51955  * @extends Roo.Component
51956  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51957  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51958  * @constructor
51959  * @param {Object} config Configuration options
51960  */
51961 Roo.form.Layout = function(config){
51962     var xitems = [];
51963     if (config.items) {
51964         xitems = config.items;
51965         delete config.items;
51966     }
51967     Roo.form.Layout.superclass.constructor.call(this, config);
51968     this.stack = [];
51969     Roo.each(xitems, this.addxtype, this);
51970      
51971 };
51972
51973 Roo.extend(Roo.form.Layout, Roo.Component, {
51974     /**
51975      * @cfg {String/Object} autoCreate
51976      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51977      */
51978     /**
51979      * @cfg {String/Object/Function} style
51980      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51981      * a function which returns such a specification.
51982      */
51983     /**
51984      * @cfg {String} labelAlign
51985      * Valid values are "left," "top" and "right" (defaults to "left")
51986      */
51987     /**
51988      * @cfg {Number} labelWidth
51989      * Fixed width in pixels of all field labels (defaults to undefined)
51990      */
51991     /**
51992      * @cfg {Boolean} clear
51993      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51994      */
51995     clear : true,
51996     /**
51997      * @cfg {String} labelSeparator
51998      * The separator to use after field labels (defaults to ':')
51999      */
52000     labelSeparator : ':',
52001     /**
52002      * @cfg {Boolean} hideLabels
52003      * True to suppress the display of field labels in this layout (defaults to false)
52004      */
52005     hideLabels : false,
52006
52007     // private
52008     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
52009     
52010     isLayout : true,
52011     
52012     // private
52013     onRender : function(ct, position){
52014         if(this.el){ // from markup
52015             this.el = Roo.get(this.el);
52016         }else {  // generate
52017             var cfg = this.getAutoCreate();
52018             this.el = ct.createChild(cfg, position);
52019         }
52020         if(this.style){
52021             this.el.applyStyles(this.style);
52022         }
52023         if(this.labelAlign){
52024             this.el.addClass('x-form-label-'+this.labelAlign);
52025         }
52026         if(this.hideLabels){
52027             this.labelStyle = "display:none";
52028             this.elementStyle = "padding-left:0;";
52029         }else{
52030             if(typeof this.labelWidth == 'number'){
52031                 this.labelStyle = "width:"+this.labelWidth+"px;";
52032                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52033             }
52034             if(this.labelAlign == 'top'){
52035                 this.labelStyle = "width:auto;";
52036                 this.elementStyle = "padding-left:0;";
52037             }
52038         }
52039         var stack = this.stack;
52040         var slen = stack.length;
52041         if(slen > 0){
52042             if(!this.fieldTpl){
52043                 var t = new Roo.Template(
52044                     '<div class="x-form-item {5}">',
52045                         '<label for="{0}" style="{2}">{1}{4}</label>',
52046                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52047                         '</div>',
52048                     '</div><div class="x-form-clear-left"></div>'
52049                 );
52050                 t.disableFormats = true;
52051                 t.compile();
52052                 Roo.form.Layout.prototype.fieldTpl = t;
52053             }
52054             for(var i = 0; i < slen; i++) {
52055                 if(stack[i].isFormField){
52056                     this.renderField(stack[i]);
52057                 }else{
52058                     this.renderComponent(stack[i]);
52059                 }
52060             }
52061         }
52062         if(this.clear){
52063             this.el.createChild({cls:'x-form-clear'});
52064         }
52065     },
52066
52067     // private
52068     renderField : function(f){
52069         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52070                f.id, //0
52071                f.fieldLabel, //1
52072                f.labelStyle||this.labelStyle||'', //2
52073                this.elementStyle||'', //3
52074                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52075                f.itemCls||this.itemCls||''  //5
52076        ], true).getPrevSibling());
52077     },
52078
52079     // private
52080     renderComponent : function(c){
52081         c.render(c.isLayout ? this.el : this.el.createChild());    
52082     },
52083     /**
52084      * Adds a object form elements (using the xtype property as the factory method.)
52085      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52086      * @param {Object} config 
52087      */
52088     addxtype : function(o)
52089     {
52090         // create the lement.
52091         o.form = this.form;
52092         var fe = Roo.factory(o, Roo.form);
52093         this.form.allItems.push(fe);
52094         this.stack.push(fe);
52095         
52096         if (fe.isFormField) {
52097             this.form.items.add(fe);
52098         }
52099          
52100         return fe;
52101     }
52102 });
52103
52104 /**
52105  * @class Roo.form.Column
52106  * @extends Roo.form.Layout
52107  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52108  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52109  * @constructor
52110  * @param {Object} config Configuration options
52111  */
52112 Roo.form.Column = function(config){
52113     Roo.form.Column.superclass.constructor.call(this, config);
52114 };
52115
52116 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52117     /**
52118      * @cfg {Number/String} width
52119      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52120      */
52121     /**
52122      * @cfg {String/Object} autoCreate
52123      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52124      */
52125
52126     // private
52127     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52128
52129     // private
52130     onRender : function(ct, position){
52131         Roo.form.Column.superclass.onRender.call(this, ct, position);
52132         if(this.width){
52133             this.el.setWidth(this.width);
52134         }
52135     }
52136 });
52137
52138
52139 /**
52140  * @class Roo.form.Row
52141  * @extends Roo.form.Layout
52142  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52143  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52144  * @constructor
52145  * @param {Object} config Configuration options
52146  */
52147
52148  
52149 Roo.form.Row = function(config){
52150     Roo.form.Row.superclass.constructor.call(this, config);
52151 };
52152  
52153 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52154       /**
52155      * @cfg {Number/String} width
52156      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52157      */
52158     /**
52159      * @cfg {Number/String} height
52160      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52161      */
52162     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52163     
52164     padWidth : 20,
52165     // private
52166     onRender : function(ct, position){
52167         //console.log('row render');
52168         if(!this.rowTpl){
52169             var t = new Roo.Template(
52170                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52171                     '<label for="{0}" style="{2}">{1}{4}</label>',
52172                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52173                     '</div>',
52174                 '</div>'
52175             );
52176             t.disableFormats = true;
52177             t.compile();
52178             Roo.form.Layout.prototype.rowTpl = t;
52179         }
52180         this.fieldTpl = this.rowTpl;
52181         
52182         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52183         var labelWidth = 100;
52184         
52185         if ((this.labelAlign != 'top')) {
52186             if (typeof this.labelWidth == 'number') {
52187                 labelWidth = this.labelWidth
52188             }
52189             this.padWidth =  20 + labelWidth;
52190             
52191         }
52192         
52193         Roo.form.Column.superclass.onRender.call(this, ct, position);
52194         if(this.width){
52195             this.el.setWidth(this.width);
52196         }
52197         if(this.height){
52198             this.el.setHeight(this.height);
52199         }
52200     },
52201     
52202     // private
52203     renderField : function(f){
52204         f.fieldEl = this.fieldTpl.append(this.el, [
52205                f.id, f.fieldLabel,
52206                f.labelStyle||this.labelStyle||'',
52207                this.elementStyle||'',
52208                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52209                f.itemCls||this.itemCls||'',
52210                f.width ? f.width + this.padWidth : 160 + this.padWidth
52211        ],true);
52212     }
52213 });
52214  
52215
52216 /**
52217  * @class Roo.form.FieldSet
52218  * @extends Roo.form.Layout
52219  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52220  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52221  * @constructor
52222  * @param {Object} config Configuration options
52223  */
52224 Roo.form.FieldSet = function(config){
52225     Roo.form.FieldSet.superclass.constructor.call(this, config);
52226 };
52227
52228 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52229     /**
52230      * @cfg {String} legend
52231      * The text to display as the legend for the FieldSet (defaults to '')
52232      */
52233     /**
52234      * @cfg {String/Object} autoCreate
52235      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52236      */
52237
52238     // private
52239     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52240
52241     // private
52242     onRender : function(ct, position){
52243         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52244         if(this.legend){
52245             this.setLegend(this.legend);
52246         }
52247     },
52248
52249     // private
52250     setLegend : function(text){
52251         if(this.rendered){
52252             this.el.child('legend').update(text);
52253         }
52254     }
52255 });/*
52256  * Based on:
52257  * Ext JS Library 1.1.1
52258  * Copyright(c) 2006-2007, Ext JS, LLC.
52259  *
52260  * Originally Released Under LGPL - original licence link has changed is not relivant.
52261  *
52262  * Fork - LGPL
52263  * <script type="text/javascript">
52264  */
52265 /**
52266  * @class Roo.form.VTypes
52267  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52268  * @static
52269  */
52270 Roo.form.VTypes = function(){
52271     // closure these in so they are only created once.
52272     var alpha = /^[a-zA-Z_]+$/;
52273     var alphanum = /^[a-zA-Z0-9_]+$/;
52274     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52275     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52276
52277     // All these messages and functions are configurable
52278     return {
52279         /**
52280          * The function used to validate email addresses
52281          * @param {String} value The email address
52282          */
52283         'email' : function(v){
52284             return email.test(v);
52285         },
52286         /**
52287          * The error text to display when the email validation function returns false
52288          * @type String
52289          */
52290         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52291         /**
52292          * The keystroke filter mask to be applied on email input
52293          * @type RegExp
52294          */
52295         'emailMask' : /[a-z0-9_\.\-@]/i,
52296
52297         /**
52298          * The function used to validate URLs
52299          * @param {String} value The URL
52300          */
52301         'url' : function(v){
52302             return url.test(v);
52303         },
52304         /**
52305          * The error text to display when the url validation function returns false
52306          * @type String
52307          */
52308         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52309         
52310         /**
52311          * The function used to validate alpha values
52312          * @param {String} value The value
52313          */
52314         'alpha' : function(v){
52315             return alpha.test(v);
52316         },
52317         /**
52318          * The error text to display when the alpha validation function returns false
52319          * @type String
52320          */
52321         'alphaText' : 'This field should only contain letters and _',
52322         /**
52323          * The keystroke filter mask to be applied on alpha input
52324          * @type RegExp
52325          */
52326         'alphaMask' : /[a-z_]/i,
52327
52328         /**
52329          * The function used to validate alphanumeric values
52330          * @param {String} value The value
52331          */
52332         'alphanum' : function(v){
52333             return alphanum.test(v);
52334         },
52335         /**
52336          * The error text to display when the alphanumeric validation function returns false
52337          * @type String
52338          */
52339         'alphanumText' : 'This field should only contain letters, numbers and _',
52340         /**
52341          * The keystroke filter mask to be applied on alphanumeric input
52342          * @type RegExp
52343          */
52344         'alphanumMask' : /[a-z0-9_]/i
52345     };
52346 }();//<script type="text/javascript">
52347
52348 /**
52349  * @class Roo.form.FCKeditor
52350  * @extends Roo.form.TextArea
52351  * Wrapper around the FCKEditor http://www.fckeditor.net
52352  * @constructor
52353  * Creates a new FCKeditor
52354  * @param {Object} config Configuration options
52355  */
52356 Roo.form.FCKeditor = function(config){
52357     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52358     this.addEvents({
52359          /**
52360          * @event editorinit
52361          * Fired when the editor is initialized - you can add extra handlers here..
52362          * @param {FCKeditor} this
52363          * @param {Object} the FCK object.
52364          */
52365         editorinit : true
52366     });
52367     
52368     
52369 };
52370 Roo.form.FCKeditor.editors = { };
52371 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52372 {
52373     //defaultAutoCreate : {
52374     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52375     //},
52376     // private
52377     /**
52378      * @cfg {Object} fck options - see fck manual for details.
52379      */
52380     fckconfig : false,
52381     
52382     /**
52383      * @cfg {Object} fck toolbar set (Basic or Default)
52384      */
52385     toolbarSet : 'Basic',
52386     /**
52387      * @cfg {Object} fck BasePath
52388      */ 
52389     basePath : '/fckeditor/',
52390     
52391     
52392     frame : false,
52393     
52394     value : '',
52395     
52396    
52397     onRender : function(ct, position)
52398     {
52399         if(!this.el){
52400             this.defaultAutoCreate = {
52401                 tag: "textarea",
52402                 style:"width:300px;height:60px;",
52403                 autocomplete: "new-password"
52404             };
52405         }
52406         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52407         /*
52408         if(this.grow){
52409             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52410             if(this.preventScrollbars){
52411                 this.el.setStyle("overflow", "hidden");
52412             }
52413             this.el.setHeight(this.growMin);
52414         }
52415         */
52416         //console.log('onrender' + this.getId() );
52417         Roo.form.FCKeditor.editors[this.getId()] = this;
52418          
52419
52420         this.replaceTextarea() ;
52421         
52422     },
52423     
52424     getEditor : function() {
52425         return this.fckEditor;
52426     },
52427     /**
52428      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52429      * @param {Mixed} value The value to set
52430      */
52431     
52432     
52433     setValue : function(value)
52434     {
52435         //console.log('setValue: ' + value);
52436         
52437         if(typeof(value) == 'undefined') { // not sure why this is happending...
52438             return;
52439         }
52440         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52441         
52442         //if(!this.el || !this.getEditor()) {
52443         //    this.value = value;
52444             //this.setValue.defer(100,this,[value]);    
52445         //    return;
52446         //} 
52447         
52448         if(!this.getEditor()) {
52449             return;
52450         }
52451         
52452         this.getEditor().SetData(value);
52453         
52454         //
52455
52456     },
52457
52458     /**
52459      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52460      * @return {Mixed} value The field value
52461      */
52462     getValue : function()
52463     {
52464         
52465         if (this.frame && this.frame.dom.style.display == 'none') {
52466             return Roo.form.FCKeditor.superclass.getValue.call(this);
52467         }
52468         
52469         if(!this.el || !this.getEditor()) {
52470            
52471            // this.getValue.defer(100,this); 
52472             return this.value;
52473         }
52474        
52475         
52476         var value=this.getEditor().GetData();
52477         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52478         return Roo.form.FCKeditor.superclass.getValue.call(this);
52479         
52480
52481     },
52482
52483     /**
52484      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52485      * @return {Mixed} value The field value
52486      */
52487     getRawValue : function()
52488     {
52489         if (this.frame && this.frame.dom.style.display == 'none') {
52490             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52491         }
52492         
52493         if(!this.el || !this.getEditor()) {
52494             //this.getRawValue.defer(100,this); 
52495             return this.value;
52496             return;
52497         }
52498         
52499         
52500         
52501         var value=this.getEditor().GetData();
52502         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52503         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52504          
52505     },
52506     
52507     setSize : function(w,h) {
52508         
52509         
52510         
52511         //if (this.frame && this.frame.dom.style.display == 'none') {
52512         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52513         //    return;
52514         //}
52515         //if(!this.el || !this.getEditor()) {
52516         //    this.setSize.defer(100,this, [w,h]); 
52517         //    return;
52518         //}
52519         
52520         
52521         
52522         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52523         
52524         this.frame.dom.setAttribute('width', w);
52525         this.frame.dom.setAttribute('height', h);
52526         this.frame.setSize(w,h);
52527         
52528     },
52529     
52530     toggleSourceEdit : function(value) {
52531         
52532       
52533          
52534         this.el.dom.style.display = value ? '' : 'none';
52535         this.frame.dom.style.display = value ?  'none' : '';
52536         
52537     },
52538     
52539     
52540     focus: function(tag)
52541     {
52542         if (this.frame.dom.style.display == 'none') {
52543             return Roo.form.FCKeditor.superclass.focus.call(this);
52544         }
52545         if(!this.el || !this.getEditor()) {
52546             this.focus.defer(100,this, [tag]); 
52547             return;
52548         }
52549         
52550         
52551         
52552         
52553         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52554         this.getEditor().Focus();
52555         if (tgs.length) {
52556             if (!this.getEditor().Selection.GetSelection()) {
52557                 this.focus.defer(100,this, [tag]); 
52558                 return;
52559             }
52560             
52561             
52562             var r = this.getEditor().EditorDocument.createRange();
52563             r.setStart(tgs[0],0);
52564             r.setEnd(tgs[0],0);
52565             this.getEditor().Selection.GetSelection().removeAllRanges();
52566             this.getEditor().Selection.GetSelection().addRange(r);
52567             this.getEditor().Focus();
52568         }
52569         
52570     },
52571     
52572     
52573     
52574     replaceTextarea : function()
52575     {
52576         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52577             return ;
52578         }
52579         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52580         //{
52581             // We must check the elements firstly using the Id and then the name.
52582         var oTextarea = document.getElementById( this.getId() );
52583         
52584         var colElementsByName = document.getElementsByName( this.getId() ) ;
52585          
52586         oTextarea.style.display = 'none' ;
52587
52588         if ( oTextarea.tabIndex ) {            
52589             this.TabIndex = oTextarea.tabIndex ;
52590         }
52591         
52592         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52593         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52594         this.frame = Roo.get(this.getId() + '___Frame')
52595     },
52596     
52597     _getConfigHtml : function()
52598     {
52599         var sConfig = '' ;
52600
52601         for ( var o in this.fckconfig ) {
52602             sConfig += sConfig.length > 0  ? '&amp;' : '';
52603             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52604         }
52605
52606         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52607     },
52608     
52609     
52610     _getIFrameHtml : function()
52611     {
52612         var sFile = 'fckeditor.html' ;
52613         /* no idea what this is about..
52614         try
52615         {
52616             if ( (/fcksource=true/i).test( window.top.location.search ) )
52617                 sFile = 'fckeditor.original.html' ;
52618         }
52619         catch (e) { 
52620         */
52621
52622         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52623         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52624         
52625         
52626         var html = '<iframe id="' + this.getId() +
52627             '___Frame" src="' + sLink +
52628             '" width="' + this.width +
52629             '" height="' + this.height + '"' +
52630             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52631             ' frameborder="0" scrolling="no"></iframe>' ;
52632
52633         return html ;
52634     },
52635     
52636     _insertHtmlBefore : function( html, element )
52637     {
52638         if ( element.insertAdjacentHTML )       {
52639             // IE
52640             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52641         } else { // Gecko
52642             var oRange = document.createRange() ;
52643             oRange.setStartBefore( element ) ;
52644             var oFragment = oRange.createContextualFragment( html );
52645             element.parentNode.insertBefore( oFragment, element ) ;
52646         }
52647     }
52648     
52649     
52650   
52651     
52652     
52653     
52654     
52655
52656 });
52657
52658 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52659
52660 function FCKeditor_OnComplete(editorInstance){
52661     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52662     f.fckEditor = editorInstance;
52663     //console.log("loaded");
52664     f.fireEvent('editorinit', f, editorInstance);
52665
52666   
52667
52668  
52669
52670
52671
52672
52673
52674
52675
52676
52677
52678
52679
52680
52681
52682
52683
52684 //<script type="text/javascript">
52685 /**
52686  * @class Roo.form.GridField
52687  * @extends Roo.form.Field
52688  * Embed a grid (or editable grid into a form)
52689  * STATUS ALPHA
52690  * 
52691  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52692  * it needs 
52693  * xgrid.store = Roo.data.Store
52694  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52695  * xgrid.store.reader = Roo.data.JsonReader 
52696  * 
52697  * 
52698  * @constructor
52699  * Creates a new GridField
52700  * @param {Object} config Configuration options
52701  */
52702 Roo.form.GridField = function(config){
52703     Roo.form.GridField.superclass.constructor.call(this, config);
52704      
52705 };
52706
52707 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52708     /**
52709      * @cfg {Number} width  - used to restrict width of grid..
52710      */
52711     width : 100,
52712     /**
52713      * @cfg {Number} height - used to restrict height of grid..
52714      */
52715     height : 50,
52716      /**
52717      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52718          * 
52719          *}
52720      */
52721     xgrid : false, 
52722     /**
52723      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52724      * {tag: "input", type: "checkbox", autocomplete: "off"})
52725      */
52726    // defaultAutoCreate : { tag: 'div' },
52727     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52728     /**
52729      * @cfg {String} addTitle Text to include for adding a title.
52730      */
52731     addTitle : false,
52732     //
52733     onResize : function(){
52734         Roo.form.Field.superclass.onResize.apply(this, arguments);
52735     },
52736
52737     initEvents : function(){
52738         // Roo.form.Checkbox.superclass.initEvents.call(this);
52739         // has no events...
52740        
52741     },
52742
52743
52744     getResizeEl : function(){
52745         return this.wrap;
52746     },
52747
52748     getPositionEl : function(){
52749         return this.wrap;
52750     },
52751
52752     // private
52753     onRender : function(ct, position){
52754         
52755         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52756         var style = this.style;
52757         delete this.style;
52758         
52759         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52760         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52761         this.viewEl = this.wrap.createChild({ tag: 'div' });
52762         if (style) {
52763             this.viewEl.applyStyles(style);
52764         }
52765         if (this.width) {
52766             this.viewEl.setWidth(this.width);
52767         }
52768         if (this.height) {
52769             this.viewEl.setHeight(this.height);
52770         }
52771         //if(this.inputValue !== undefined){
52772         //this.setValue(this.value);
52773         
52774         
52775         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52776         
52777         
52778         this.grid.render();
52779         this.grid.getDataSource().on('remove', this.refreshValue, this);
52780         this.grid.getDataSource().on('update', this.refreshValue, this);
52781         this.grid.on('afteredit', this.refreshValue, this);
52782  
52783     },
52784      
52785     
52786     /**
52787      * Sets the value of the item. 
52788      * @param {String} either an object  or a string..
52789      */
52790     setValue : function(v){
52791         //this.value = v;
52792         v = v || []; // empty set..
52793         // this does not seem smart - it really only affects memoryproxy grids..
52794         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52795             var ds = this.grid.getDataSource();
52796             // assumes a json reader..
52797             var data = {}
52798             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52799             ds.loadData( data);
52800         }
52801         // clear selection so it does not get stale.
52802         if (this.grid.sm) { 
52803             this.grid.sm.clearSelections();
52804         }
52805         
52806         Roo.form.GridField.superclass.setValue.call(this, v);
52807         this.refreshValue();
52808         // should load data in the grid really....
52809     },
52810     
52811     // private
52812     refreshValue: function() {
52813          var val = [];
52814         this.grid.getDataSource().each(function(r) {
52815             val.push(r.data);
52816         });
52817         this.el.dom.value = Roo.encode(val);
52818     }
52819     
52820      
52821     
52822     
52823 });/*
52824  * Based on:
52825  * Ext JS Library 1.1.1
52826  * Copyright(c) 2006-2007, Ext JS, LLC.
52827  *
52828  * Originally Released Under LGPL - original licence link has changed is not relivant.
52829  *
52830  * Fork - LGPL
52831  * <script type="text/javascript">
52832  */
52833 /**
52834  * @class Roo.form.DisplayField
52835  * @extends Roo.form.Field
52836  * A generic Field to display non-editable data.
52837  * @cfg {Boolean} closable (true|false) default false
52838  * @constructor
52839  * Creates a new Display Field item.
52840  * @param {Object} config Configuration options
52841  */
52842 Roo.form.DisplayField = function(config){
52843     Roo.form.DisplayField.superclass.constructor.call(this, config);
52844     
52845     this.addEvents({
52846         /**
52847          * @event close
52848          * Fires after the click the close btn
52849              * @param {Roo.form.DisplayField} this
52850              */
52851         close : true
52852     });
52853 };
52854
52855 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52856     inputType:      'hidden',
52857     allowBlank:     true,
52858     readOnly:         true,
52859     
52860  
52861     /**
52862      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52863      */
52864     focusClass : undefined,
52865     /**
52866      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52867      */
52868     fieldClass: 'x-form-field',
52869     
52870      /**
52871      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52872      */
52873     valueRenderer: undefined,
52874     
52875     width: 100,
52876     /**
52877      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52878      * {tag: "input", type: "checkbox", autocomplete: "off"})
52879      */
52880      
52881  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52882  
52883     closable : false,
52884     
52885     onResize : function(){
52886         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52887         
52888     },
52889
52890     initEvents : function(){
52891         // Roo.form.Checkbox.superclass.initEvents.call(this);
52892         // has no events...
52893         
52894         if(this.closable){
52895             this.closeEl.on('click', this.onClose, this);
52896         }
52897        
52898     },
52899
52900
52901     getResizeEl : function(){
52902         return this.wrap;
52903     },
52904
52905     getPositionEl : function(){
52906         return this.wrap;
52907     },
52908
52909     // private
52910     onRender : function(ct, position){
52911         
52912         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52913         //if(this.inputValue !== undefined){
52914         this.wrap = this.el.wrap();
52915         
52916         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52917         
52918         if(this.closable){
52919             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52920         }
52921         
52922         if (this.bodyStyle) {
52923             this.viewEl.applyStyles(this.bodyStyle);
52924         }
52925         //this.viewEl.setStyle('padding', '2px');
52926         
52927         this.setValue(this.value);
52928         
52929     },
52930 /*
52931     // private
52932     initValue : Roo.emptyFn,
52933
52934   */
52935
52936         // private
52937     onClick : function(){
52938         
52939     },
52940
52941     /**
52942      * Sets the checked state of the checkbox.
52943      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52944      */
52945     setValue : function(v){
52946         this.value = v;
52947         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52948         // this might be called before we have a dom element..
52949         if (!this.viewEl) {
52950             return;
52951         }
52952         this.viewEl.dom.innerHTML = html;
52953         Roo.form.DisplayField.superclass.setValue.call(this, v);
52954
52955     },
52956     
52957     onClose : function(e)
52958     {
52959         e.preventDefault();
52960         
52961         this.fireEvent('close', this);
52962     }
52963 });/*
52964  * 
52965  * Licence- LGPL
52966  * 
52967  */
52968
52969 /**
52970  * @class Roo.form.DayPicker
52971  * @extends Roo.form.Field
52972  * A Day picker show [M] [T] [W] ....
52973  * @constructor
52974  * Creates a new Day Picker
52975  * @param {Object} config Configuration options
52976  */
52977 Roo.form.DayPicker= function(config){
52978     Roo.form.DayPicker.superclass.constructor.call(this, config);
52979      
52980 };
52981
52982 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52983     /**
52984      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52985      */
52986     focusClass : undefined,
52987     /**
52988      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52989      */
52990     fieldClass: "x-form-field",
52991    
52992     /**
52993      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52994      * {tag: "input", type: "checkbox", autocomplete: "off"})
52995      */
52996     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52997     
52998    
52999     actionMode : 'viewEl', 
53000     //
53001     // private
53002  
53003     inputType : 'hidden',
53004     
53005      
53006     inputElement: false, // real input element?
53007     basedOn: false, // ????
53008     
53009     isFormField: true, // not sure where this is needed!!!!
53010
53011     onResize : function(){
53012         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
53013         if(!this.boxLabel){
53014             this.el.alignTo(this.wrap, 'c-c');
53015         }
53016     },
53017
53018     initEvents : function(){
53019         Roo.form.Checkbox.superclass.initEvents.call(this);
53020         this.el.on("click", this.onClick,  this);
53021         this.el.on("change", this.onClick,  this);
53022     },
53023
53024
53025     getResizeEl : function(){
53026         return this.wrap;
53027     },
53028
53029     getPositionEl : function(){
53030         return this.wrap;
53031     },
53032
53033     
53034     // private
53035     onRender : function(ct, position){
53036         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53037        
53038         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53039         
53040         var r1 = '<table><tr>';
53041         var r2 = '<tr class="x-form-daypick-icons">';
53042         for (var i=0; i < 7; i++) {
53043             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53044             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53045         }
53046         
53047         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53048         viewEl.select('img').on('click', this.onClick, this);
53049         this.viewEl = viewEl;   
53050         
53051         
53052         // this will not work on Chrome!!!
53053         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53054         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53055         
53056         
53057           
53058
53059     },
53060
53061     // private
53062     initValue : Roo.emptyFn,
53063
53064     /**
53065      * Returns the checked state of the checkbox.
53066      * @return {Boolean} True if checked, else false
53067      */
53068     getValue : function(){
53069         return this.el.dom.value;
53070         
53071     },
53072
53073         // private
53074     onClick : function(e){ 
53075         //this.setChecked(!this.checked);
53076         Roo.get(e.target).toggleClass('x-menu-item-checked');
53077         this.refreshValue();
53078         //if(this.el.dom.checked != this.checked){
53079         //    this.setValue(this.el.dom.checked);
53080        // }
53081     },
53082     
53083     // private
53084     refreshValue : function()
53085     {
53086         var val = '';
53087         this.viewEl.select('img',true).each(function(e,i,n)  {
53088             val += e.is(".x-menu-item-checked") ? String(n) : '';
53089         });
53090         this.setValue(val, true);
53091     },
53092
53093     /**
53094      * Sets the checked state of the checkbox.
53095      * On is always based on a string comparison between inputValue and the param.
53096      * @param {Boolean/String} value - the value to set 
53097      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53098      */
53099     setValue : function(v,suppressEvent){
53100         if (!this.el.dom) {
53101             return;
53102         }
53103         var old = this.el.dom.value ;
53104         this.el.dom.value = v;
53105         if (suppressEvent) {
53106             return ;
53107         }
53108          
53109         // update display..
53110         this.viewEl.select('img',true).each(function(e,i,n)  {
53111             
53112             var on = e.is(".x-menu-item-checked");
53113             var newv = v.indexOf(String(n)) > -1;
53114             if (on != newv) {
53115                 e.toggleClass('x-menu-item-checked');
53116             }
53117             
53118         });
53119         
53120         
53121         this.fireEvent('change', this, v, old);
53122         
53123         
53124     },
53125    
53126     // handle setting of hidden value by some other method!!?!?
53127     setFromHidden: function()
53128     {
53129         if(!this.el){
53130             return;
53131         }
53132         //console.log("SET FROM HIDDEN");
53133         //alert('setFrom hidden');
53134         this.setValue(this.el.dom.value);
53135     },
53136     
53137     onDestroy : function()
53138     {
53139         if(this.viewEl){
53140             Roo.get(this.viewEl).remove();
53141         }
53142          
53143         Roo.form.DayPicker.superclass.onDestroy.call(this);
53144     }
53145
53146 });/*
53147  * RooJS Library 1.1.1
53148  * Copyright(c) 2008-2011  Alan Knowles
53149  *
53150  * License - LGPL
53151  */
53152  
53153
53154 /**
53155  * @class Roo.form.ComboCheck
53156  * @extends Roo.form.ComboBox
53157  * A combobox for multiple select items.
53158  *
53159  * FIXME - could do with a reset button..
53160  * 
53161  * @constructor
53162  * Create a new ComboCheck
53163  * @param {Object} config Configuration options
53164  */
53165 Roo.form.ComboCheck = function(config){
53166     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53167     // should verify some data...
53168     // like
53169     // hiddenName = required..
53170     // displayField = required
53171     // valudField == required
53172     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53173     var _t = this;
53174     Roo.each(req, function(e) {
53175         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53176             throw "Roo.form.ComboCheck : missing value for: " + e;
53177         }
53178     });
53179     
53180     
53181 };
53182
53183 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53184      
53185      
53186     editable : false,
53187      
53188     selectedClass: 'x-menu-item-checked', 
53189     
53190     // private
53191     onRender : function(ct, position){
53192         var _t = this;
53193         
53194         
53195         
53196         if(!this.tpl){
53197             var cls = 'x-combo-list';
53198
53199             
53200             this.tpl =  new Roo.Template({
53201                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53202                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53203                    '<span>{' + this.displayField + '}</span>' +
53204                     '</div>' 
53205                 
53206             });
53207         }
53208  
53209         
53210         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53211         this.view.singleSelect = false;
53212         this.view.multiSelect = true;
53213         this.view.toggleSelect = true;
53214         this.pageTb.add(new Roo.Toolbar.Fill(), {
53215             
53216             text: 'Done',
53217             handler: function()
53218             {
53219                 _t.collapse();
53220             }
53221         });
53222     },
53223     
53224     onViewOver : function(e, t){
53225         // do nothing...
53226         return;
53227         
53228     },
53229     
53230     onViewClick : function(doFocus,index){
53231         return;
53232         
53233     },
53234     select: function () {
53235         //Roo.log("SELECT CALLED");
53236     },
53237      
53238     selectByValue : function(xv, scrollIntoView){
53239         var ar = this.getValueArray();
53240         var sels = [];
53241         
53242         Roo.each(ar, function(v) {
53243             if(v === undefined || v === null){
53244                 return;
53245             }
53246             var r = this.findRecord(this.valueField, v);
53247             if(r){
53248                 sels.push(this.store.indexOf(r))
53249                 
53250             }
53251         },this);
53252         this.view.select(sels);
53253         return false;
53254     },
53255     
53256     
53257     
53258     onSelect : function(record, index){
53259        // Roo.log("onselect Called");
53260        // this is only called by the clear button now..
53261         this.view.clearSelections();
53262         this.setValue('[]');
53263         if (this.value != this.valueBefore) {
53264             this.fireEvent('change', this, this.value, this.valueBefore);
53265             this.valueBefore = this.value;
53266         }
53267     },
53268     getValueArray : function()
53269     {
53270         var ar = [] ;
53271         
53272         try {
53273             //Roo.log(this.value);
53274             if (typeof(this.value) == 'undefined') {
53275                 return [];
53276             }
53277             var ar = Roo.decode(this.value);
53278             return  ar instanceof Array ? ar : []; //?? valid?
53279             
53280         } catch(e) {
53281             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53282             return [];
53283         }
53284          
53285     },
53286     expand : function ()
53287     {
53288         
53289         Roo.form.ComboCheck.superclass.expand.call(this);
53290         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53291         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53292         
53293
53294     },
53295     
53296     collapse : function(){
53297         Roo.form.ComboCheck.superclass.collapse.call(this);
53298         var sl = this.view.getSelectedIndexes();
53299         var st = this.store;
53300         var nv = [];
53301         var tv = [];
53302         var r;
53303         Roo.each(sl, function(i) {
53304             r = st.getAt(i);
53305             nv.push(r.get(this.valueField));
53306         },this);
53307         this.setValue(Roo.encode(nv));
53308         if (this.value != this.valueBefore) {
53309
53310             this.fireEvent('change', this, this.value, this.valueBefore);
53311             this.valueBefore = this.value;
53312         }
53313         
53314     },
53315     
53316     setValue : function(v){
53317         // Roo.log(v);
53318         this.value = v;
53319         
53320         var vals = this.getValueArray();
53321         var tv = [];
53322         Roo.each(vals, function(k) {
53323             var r = this.findRecord(this.valueField, k);
53324             if(r){
53325                 tv.push(r.data[this.displayField]);
53326             }else if(this.valueNotFoundText !== undefined){
53327                 tv.push( this.valueNotFoundText );
53328             }
53329         },this);
53330        // Roo.log(tv);
53331         
53332         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53333         this.hiddenField.value = v;
53334         this.value = v;
53335     }
53336     
53337 });/*
53338  * Based on:
53339  * Ext JS Library 1.1.1
53340  * Copyright(c) 2006-2007, Ext JS, LLC.
53341  *
53342  * Originally Released Under LGPL - original licence link has changed is not relivant.
53343  *
53344  * Fork - LGPL
53345  * <script type="text/javascript">
53346  */
53347  
53348 /**
53349  * @class Roo.form.Signature
53350  * @extends Roo.form.Field
53351  * Signature field.  
53352  * @constructor
53353  * 
53354  * @param {Object} config Configuration options
53355  */
53356
53357 Roo.form.Signature = function(config){
53358     Roo.form.Signature.superclass.constructor.call(this, config);
53359     
53360     this.addEvents({// not in used??
53361          /**
53362          * @event confirm
53363          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53364              * @param {Roo.form.Signature} combo This combo box
53365              */
53366         'confirm' : true,
53367         /**
53368          * @event reset
53369          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53370              * @param {Roo.form.ComboBox} combo This combo box
53371              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53372              */
53373         'reset' : true
53374     });
53375 };
53376
53377 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53378     /**
53379      * @cfg {Object} labels Label to use when rendering a form.
53380      * defaults to 
53381      * labels : { 
53382      *      clear : "Clear",
53383      *      confirm : "Confirm"
53384      *  }
53385      */
53386     labels : { 
53387         clear : "Clear",
53388         confirm : "Confirm"
53389     },
53390     /**
53391      * @cfg {Number} width The signature panel width (defaults to 300)
53392      */
53393     width: 300,
53394     /**
53395      * @cfg {Number} height The signature panel height (defaults to 100)
53396      */
53397     height : 100,
53398     /**
53399      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53400      */
53401     allowBlank : false,
53402     
53403     //private
53404     // {Object} signPanel The signature SVG panel element (defaults to {})
53405     signPanel : {},
53406     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53407     isMouseDown : false,
53408     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53409     isConfirmed : false,
53410     // {String} signatureTmp SVG mapping string (defaults to empty string)
53411     signatureTmp : '',
53412     
53413     
53414     defaultAutoCreate : { // modified by initCompnoent..
53415         tag: "input",
53416         type:"hidden"
53417     },
53418
53419     // private
53420     onRender : function(ct, position){
53421         
53422         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53423         
53424         this.wrap = this.el.wrap({
53425             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53426         });
53427         
53428         this.createToolbar(this);
53429         this.signPanel = this.wrap.createChild({
53430                 tag: 'div',
53431                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53432             }, this.el
53433         );
53434             
53435         this.svgID = Roo.id();
53436         this.svgEl = this.signPanel.createChild({
53437               xmlns : 'http://www.w3.org/2000/svg',
53438               tag : 'svg',
53439               id : this.svgID + "-svg",
53440               width: this.width,
53441               height: this.height,
53442               viewBox: '0 0 '+this.width+' '+this.height,
53443               cn : [
53444                 {
53445                     tag: "rect",
53446                     id: this.svgID + "-svg-r",
53447                     width: this.width,
53448                     height: this.height,
53449                     fill: "#ffa"
53450                 },
53451                 {
53452                     tag: "line",
53453                     id: this.svgID + "-svg-l",
53454                     x1: "0", // start
53455                     y1: (this.height*0.8), // start set the line in 80% of height
53456                     x2: this.width, // end
53457                     y2: (this.height*0.8), // end set the line in 80% of height
53458                     'stroke': "#666",
53459                     'stroke-width': "1",
53460                     'stroke-dasharray': "3",
53461                     'shape-rendering': "crispEdges",
53462                     'pointer-events': "none"
53463                 },
53464                 {
53465                     tag: "path",
53466                     id: this.svgID + "-svg-p",
53467                     'stroke': "navy",
53468                     'stroke-width': "3",
53469                     'fill': "none",
53470                     'pointer-events': 'none'
53471                 }
53472               ]
53473         });
53474         this.createSVG();
53475         this.svgBox = this.svgEl.dom.getScreenCTM();
53476     },
53477     createSVG : function(){ 
53478         var svg = this.signPanel;
53479         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53480         var t = this;
53481
53482         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53483         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53484         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53485         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53486         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53487         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53488         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53489         
53490     },
53491     isTouchEvent : function(e){
53492         return e.type.match(/^touch/);
53493     },
53494     getCoords : function (e) {
53495         var pt    = this.svgEl.dom.createSVGPoint();
53496         pt.x = e.clientX; 
53497         pt.y = e.clientY;
53498         if (this.isTouchEvent(e)) {
53499             pt.x =  e.targetTouches[0].clientX;
53500             pt.y = e.targetTouches[0].clientY;
53501         }
53502         var a = this.svgEl.dom.getScreenCTM();
53503         var b = a.inverse();
53504         var mx = pt.matrixTransform(b);
53505         return mx.x + ',' + mx.y;
53506     },
53507     //mouse event headler 
53508     down : function (e) {
53509         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53510         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53511         
53512         this.isMouseDown = true;
53513         
53514         e.preventDefault();
53515     },
53516     move : function (e) {
53517         if (this.isMouseDown) {
53518             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53519             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53520         }
53521         
53522         e.preventDefault();
53523     },
53524     up : function (e) {
53525         this.isMouseDown = false;
53526         var sp = this.signatureTmp.split(' ');
53527         
53528         if(sp.length > 1){
53529             if(!sp[sp.length-2].match(/^L/)){
53530                 sp.pop();
53531                 sp.pop();
53532                 sp.push("");
53533                 this.signatureTmp = sp.join(" ");
53534             }
53535         }
53536         if(this.getValue() != this.signatureTmp){
53537             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53538             this.isConfirmed = false;
53539         }
53540         e.preventDefault();
53541     },
53542     
53543     /**
53544      * Protected method that will not generally be called directly. It
53545      * is called when the editor creates its toolbar. Override this method if you need to
53546      * add custom toolbar buttons.
53547      * @param {HtmlEditor} editor
53548      */
53549     createToolbar : function(editor){
53550          function btn(id, toggle, handler){
53551             var xid = fid + '-'+ id ;
53552             return {
53553                 id : xid,
53554                 cmd : id,
53555                 cls : 'x-btn-icon x-edit-'+id,
53556                 enableToggle:toggle !== false,
53557                 scope: editor, // was editor...
53558                 handler:handler||editor.relayBtnCmd,
53559                 clickEvent:'mousedown',
53560                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53561                 tabIndex:-1
53562             };
53563         }
53564         
53565         
53566         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53567         this.tb = tb;
53568         this.tb.add(
53569            {
53570                 cls : ' x-signature-btn x-signature-'+id,
53571                 scope: editor, // was editor...
53572                 handler: this.reset,
53573                 clickEvent:'mousedown',
53574                 text: this.labels.clear
53575             },
53576             {
53577                  xtype : 'Fill',
53578                  xns: Roo.Toolbar
53579             }, 
53580             {
53581                 cls : '  x-signature-btn x-signature-'+id,
53582                 scope: editor, // was editor...
53583                 handler: this.confirmHandler,
53584                 clickEvent:'mousedown',
53585                 text: this.labels.confirm
53586             }
53587         );
53588     
53589     },
53590     //public
53591     /**
53592      * when user is clicked confirm then show this image.....
53593      * 
53594      * @return {String} Image Data URI
53595      */
53596     getImageDataURI : function(){
53597         var svg = this.svgEl.dom.parentNode.innerHTML;
53598         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53599         return src; 
53600     },
53601     /**
53602      * 
53603      * @return {Boolean} this.isConfirmed
53604      */
53605     getConfirmed : function(){
53606         return this.isConfirmed;
53607     },
53608     /**
53609      * 
53610      * @return {Number} this.width
53611      */
53612     getWidth : function(){
53613         return this.width;
53614     },
53615     /**
53616      * 
53617      * @return {Number} this.height
53618      */
53619     getHeight : function(){
53620         return this.height;
53621     },
53622     // private
53623     getSignature : function(){
53624         return this.signatureTmp;
53625     },
53626     // private
53627     reset : function(){
53628         this.signatureTmp = '';
53629         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53630         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53631         this.isConfirmed = false;
53632         Roo.form.Signature.superclass.reset.call(this);
53633     },
53634     setSignature : function(s){
53635         this.signatureTmp = s;
53636         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53637         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53638         this.setValue(s);
53639         this.isConfirmed = false;
53640         Roo.form.Signature.superclass.reset.call(this);
53641     }, 
53642     test : function(){
53643 //        Roo.log(this.signPanel.dom.contentWindow.up())
53644     },
53645     //private
53646     setConfirmed : function(){
53647         
53648         
53649         
53650 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53651     },
53652     // private
53653     confirmHandler : function(){
53654         if(!this.getSignature()){
53655             return;
53656         }
53657         
53658         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53659         this.setValue(this.getSignature());
53660         this.isConfirmed = true;
53661         
53662         this.fireEvent('confirm', this);
53663     },
53664     // private
53665     // Subclasses should provide the validation implementation by overriding this
53666     validateValue : function(value){
53667         if(this.allowBlank){
53668             return true;
53669         }
53670         
53671         if(this.isConfirmed){
53672             return true;
53673         }
53674         return false;
53675     }
53676 });/*
53677  * Based on:
53678  * Ext JS Library 1.1.1
53679  * Copyright(c) 2006-2007, Ext JS, LLC.
53680  *
53681  * Originally Released Under LGPL - original licence link has changed is not relivant.
53682  *
53683  * Fork - LGPL
53684  * <script type="text/javascript">
53685  */
53686  
53687
53688 /**
53689  * @class Roo.form.ComboBox
53690  * @extends Roo.form.TriggerField
53691  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53692  * @constructor
53693  * Create a new ComboBox.
53694  * @param {Object} config Configuration options
53695  */
53696 Roo.form.Select = function(config){
53697     Roo.form.Select.superclass.constructor.call(this, config);
53698      
53699 };
53700
53701 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53702     /**
53703      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53704      */
53705     /**
53706      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53707      * rendering into an Roo.Editor, defaults to false)
53708      */
53709     /**
53710      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53711      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53712      */
53713     /**
53714      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53715      */
53716     /**
53717      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53718      * the dropdown list (defaults to undefined, with no header element)
53719      */
53720
53721      /**
53722      * @cfg {String/Roo.Template} tpl The template to use to render the output
53723      */
53724      
53725     // private
53726     defaultAutoCreate : {tag: "select"  },
53727     /**
53728      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53729      */
53730     listWidth: undefined,
53731     /**
53732      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53733      * mode = 'remote' or 'text' if mode = 'local')
53734      */
53735     displayField: undefined,
53736     /**
53737      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53738      * mode = 'remote' or 'value' if mode = 'local'). 
53739      * Note: use of a valueField requires the user make a selection
53740      * in order for a value to be mapped.
53741      */
53742     valueField: undefined,
53743     
53744     
53745     /**
53746      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53747      * field's data value (defaults to the underlying DOM element's name)
53748      */
53749     hiddenName: undefined,
53750     /**
53751      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53752      */
53753     listClass: '',
53754     /**
53755      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53756      */
53757     selectedClass: 'x-combo-selected',
53758     /**
53759      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53760      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53761      * which displays a downward arrow icon).
53762      */
53763     triggerClass : 'x-form-arrow-trigger',
53764     /**
53765      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53766      */
53767     shadow:'sides',
53768     /**
53769      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53770      * anchor positions (defaults to 'tl-bl')
53771      */
53772     listAlign: 'tl-bl?',
53773     /**
53774      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53775      */
53776     maxHeight: 300,
53777     /**
53778      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53779      * query specified by the allQuery config option (defaults to 'query')
53780      */
53781     triggerAction: 'query',
53782     /**
53783      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53784      * (defaults to 4, does not apply if editable = false)
53785      */
53786     minChars : 4,
53787     /**
53788      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53789      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53790      */
53791     typeAhead: false,
53792     /**
53793      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53794      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53795      */
53796     queryDelay: 500,
53797     /**
53798      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53799      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53800      */
53801     pageSize: 0,
53802     /**
53803      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53804      * when editable = true (defaults to false)
53805      */
53806     selectOnFocus:false,
53807     /**
53808      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53809      */
53810     queryParam: 'query',
53811     /**
53812      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53813      * when mode = 'remote' (defaults to 'Loading...')
53814      */
53815     loadingText: 'Loading...',
53816     /**
53817      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53818      */
53819     resizable: false,
53820     /**
53821      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53822      */
53823     handleHeight : 8,
53824     /**
53825      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53826      * traditional select (defaults to true)
53827      */
53828     editable: true,
53829     /**
53830      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53831      */
53832     allQuery: '',
53833     /**
53834      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53835      */
53836     mode: 'remote',
53837     /**
53838      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53839      * listWidth has a higher value)
53840      */
53841     minListWidth : 70,
53842     /**
53843      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53844      * allow the user to set arbitrary text into the field (defaults to false)
53845      */
53846     forceSelection:false,
53847     /**
53848      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53849      * if typeAhead = true (defaults to 250)
53850      */
53851     typeAheadDelay : 250,
53852     /**
53853      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53854      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53855      */
53856     valueNotFoundText : undefined,
53857     
53858     /**
53859      * @cfg {String} defaultValue The value displayed after loading the store.
53860      */
53861     defaultValue: '',
53862     
53863     /**
53864      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53865      */
53866     blockFocus : false,
53867     
53868     /**
53869      * @cfg {Boolean} disableClear Disable showing of clear button.
53870      */
53871     disableClear : false,
53872     /**
53873      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53874      */
53875     alwaysQuery : false,
53876     
53877     //private
53878     addicon : false,
53879     editicon: false,
53880     
53881     // element that contains real text value.. (when hidden is used..)
53882      
53883     // private
53884     onRender : function(ct, position){
53885         Roo.form.Field.prototype.onRender.call(this, ct, position);
53886         
53887         if(this.store){
53888             this.store.on('beforeload', this.onBeforeLoad, this);
53889             this.store.on('load', this.onLoad, this);
53890             this.store.on('loadexception', this.onLoadException, this);
53891             this.store.load({});
53892         }
53893         
53894         
53895         
53896     },
53897
53898     // private
53899     initEvents : function(){
53900         //Roo.form.ComboBox.superclass.initEvents.call(this);
53901  
53902     },
53903
53904     onDestroy : function(){
53905        
53906         if(this.store){
53907             this.store.un('beforeload', this.onBeforeLoad, this);
53908             this.store.un('load', this.onLoad, this);
53909             this.store.un('loadexception', this.onLoadException, this);
53910         }
53911         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53912     },
53913
53914     // private
53915     fireKey : function(e){
53916         if(e.isNavKeyPress() && !this.list.isVisible()){
53917             this.fireEvent("specialkey", this, e);
53918         }
53919     },
53920
53921     // private
53922     onResize: function(w, h){
53923         
53924         return; 
53925     
53926         
53927     },
53928
53929     /**
53930      * Allow or prevent the user from directly editing the field text.  If false is passed,
53931      * the user will only be able to select from the items defined in the dropdown list.  This method
53932      * is the runtime equivalent of setting the 'editable' config option at config time.
53933      * @param {Boolean} value True to allow the user to directly edit the field text
53934      */
53935     setEditable : function(value){
53936          
53937     },
53938
53939     // private
53940     onBeforeLoad : function(){
53941         
53942         Roo.log("Select before load");
53943         return;
53944     
53945         this.innerList.update(this.loadingText ?
53946                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53947         //this.restrictHeight();
53948         this.selectedIndex = -1;
53949     },
53950
53951     // private
53952     onLoad : function(){
53953
53954     
53955         var dom = this.el.dom;
53956         dom.innerHTML = '';
53957          var od = dom.ownerDocument;
53958          
53959         if (this.emptyText) {
53960             var op = od.createElement('option');
53961             op.setAttribute('value', '');
53962             op.innerHTML = String.format('{0}', this.emptyText);
53963             dom.appendChild(op);
53964         }
53965         if(this.store.getCount() > 0){
53966            
53967             var vf = this.valueField;
53968             var df = this.displayField;
53969             this.store.data.each(function(r) {
53970                 // which colmsn to use... testing - cdoe / title..
53971                 var op = od.createElement('option');
53972                 op.setAttribute('value', r.data[vf]);
53973                 op.innerHTML = String.format('{0}', r.data[df]);
53974                 dom.appendChild(op);
53975             });
53976             if (typeof(this.defaultValue != 'undefined')) {
53977                 this.setValue(this.defaultValue);
53978             }
53979             
53980              
53981         }else{
53982             //this.onEmptyResults();
53983         }
53984         //this.el.focus();
53985     },
53986     // private
53987     onLoadException : function()
53988     {
53989         dom.innerHTML = '';
53990             
53991         Roo.log("Select on load exception");
53992         return;
53993     
53994         this.collapse();
53995         Roo.log(this.store.reader.jsonData);
53996         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53997             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53998         }
53999         
54000         
54001     },
54002     // private
54003     onTypeAhead : function(){
54004          
54005     },
54006
54007     // private
54008     onSelect : function(record, index){
54009         Roo.log('on select?');
54010         return;
54011         if(this.fireEvent('beforeselect', this, record, index) !== false){
54012             this.setFromData(index > -1 ? record.data : false);
54013             this.collapse();
54014             this.fireEvent('select', this, record, index);
54015         }
54016     },
54017
54018     /**
54019      * Returns the currently selected field value or empty string if no value is set.
54020      * @return {String} value The selected value
54021      */
54022     getValue : function(){
54023         var dom = this.el.dom;
54024         this.value = dom.options[dom.selectedIndex].value;
54025         return this.value;
54026         
54027     },
54028
54029     /**
54030      * Clears any text/value currently set in the field
54031      */
54032     clearValue : function(){
54033         this.value = '';
54034         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54035         
54036     },
54037
54038     /**
54039      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54040      * will be displayed in the field.  If the value does not match the data value of an existing item,
54041      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54042      * Otherwise the field will be blank (although the value will still be set).
54043      * @param {String} value The value to match
54044      */
54045     setValue : function(v){
54046         var d = this.el.dom;
54047         for (var i =0; i < d.options.length;i++) {
54048             if (v == d.options[i].value) {
54049                 d.selectedIndex = i;
54050                 this.value = v;
54051                 return;
54052             }
54053         }
54054         this.clearValue();
54055     },
54056     /**
54057      * @property {Object} the last set data for the element
54058      */
54059     
54060     lastData : false,
54061     /**
54062      * Sets the value of the field based on a object which is related to the record format for the store.
54063      * @param {Object} value the value to set as. or false on reset?
54064      */
54065     setFromData : function(o){
54066         Roo.log('setfrom data?');
54067          
54068         
54069         
54070     },
54071     // private
54072     reset : function(){
54073         this.clearValue();
54074     },
54075     // private
54076     findRecord : function(prop, value){
54077         
54078         return false;
54079     
54080         var record;
54081         if(this.store.getCount() > 0){
54082             this.store.each(function(r){
54083                 if(r.data[prop] == value){
54084                     record = r;
54085                     return false;
54086                 }
54087                 return true;
54088             });
54089         }
54090         return record;
54091     },
54092     
54093     getName: function()
54094     {
54095         // returns hidden if it's set..
54096         if (!this.rendered) {return ''};
54097         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54098         
54099     },
54100      
54101
54102     
54103
54104     // private
54105     onEmptyResults : function(){
54106         Roo.log('empty results');
54107         //this.collapse();
54108     },
54109
54110     /**
54111      * Returns true if the dropdown list is expanded, else false.
54112      */
54113     isExpanded : function(){
54114         return false;
54115     },
54116
54117     /**
54118      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54119      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54120      * @param {String} value The data value of the item to select
54121      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54122      * selected item if it is not currently in view (defaults to true)
54123      * @return {Boolean} True if the value matched an item in the list, else false
54124      */
54125     selectByValue : function(v, scrollIntoView){
54126         Roo.log('select By Value');
54127         return false;
54128     
54129         if(v !== undefined && v !== null){
54130             var r = this.findRecord(this.valueField || this.displayField, v);
54131             if(r){
54132                 this.select(this.store.indexOf(r), scrollIntoView);
54133                 return true;
54134             }
54135         }
54136         return false;
54137     },
54138
54139     /**
54140      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54141      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54142      * @param {Number} index The zero-based index of the list item to select
54143      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54144      * selected item if it is not currently in view (defaults to true)
54145      */
54146     select : function(index, scrollIntoView){
54147         Roo.log('select ');
54148         return  ;
54149         
54150         this.selectedIndex = index;
54151         this.view.select(index);
54152         if(scrollIntoView !== false){
54153             var el = this.view.getNode(index);
54154             if(el){
54155                 this.innerList.scrollChildIntoView(el, false);
54156             }
54157         }
54158     },
54159
54160       
54161
54162     // private
54163     validateBlur : function(){
54164         
54165         return;
54166         
54167     },
54168
54169     // private
54170     initQuery : function(){
54171         this.doQuery(this.getRawValue());
54172     },
54173
54174     // private
54175     doForce : function(){
54176         if(this.el.dom.value.length > 0){
54177             this.el.dom.value =
54178                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54179              
54180         }
54181     },
54182
54183     /**
54184      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54185      * query allowing the query action to be canceled if needed.
54186      * @param {String} query The SQL query to execute
54187      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54188      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54189      * saved in the current store (defaults to false)
54190      */
54191     doQuery : function(q, forceAll){
54192         
54193         Roo.log('doQuery?');
54194         if(q === undefined || q === null){
54195             q = '';
54196         }
54197         var qe = {
54198             query: q,
54199             forceAll: forceAll,
54200             combo: this,
54201             cancel:false
54202         };
54203         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54204             return false;
54205         }
54206         q = qe.query;
54207         forceAll = qe.forceAll;
54208         if(forceAll === true || (q.length >= this.minChars)){
54209             if(this.lastQuery != q || this.alwaysQuery){
54210                 this.lastQuery = q;
54211                 if(this.mode == 'local'){
54212                     this.selectedIndex = -1;
54213                     if(forceAll){
54214                         this.store.clearFilter();
54215                     }else{
54216                         this.store.filter(this.displayField, q);
54217                     }
54218                     this.onLoad();
54219                 }else{
54220                     this.store.baseParams[this.queryParam] = q;
54221                     this.store.load({
54222                         params: this.getParams(q)
54223                     });
54224                     this.expand();
54225                 }
54226             }else{
54227                 this.selectedIndex = -1;
54228                 this.onLoad();   
54229             }
54230         }
54231     },
54232
54233     // private
54234     getParams : function(q){
54235         var p = {};
54236         //p[this.queryParam] = q;
54237         if(this.pageSize){
54238             p.start = 0;
54239             p.limit = this.pageSize;
54240         }
54241         return p;
54242     },
54243
54244     /**
54245      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54246      */
54247     collapse : function(){
54248         
54249     },
54250
54251     // private
54252     collapseIf : function(e){
54253         
54254     },
54255
54256     /**
54257      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54258      */
54259     expand : function(){
54260         
54261     } ,
54262
54263     // private
54264      
54265
54266     /** 
54267     * @cfg {Boolean} grow 
54268     * @hide 
54269     */
54270     /** 
54271     * @cfg {Number} growMin 
54272     * @hide 
54273     */
54274     /** 
54275     * @cfg {Number} growMax 
54276     * @hide 
54277     */
54278     /**
54279      * @hide
54280      * @method autoSize
54281      */
54282     
54283     setWidth : function()
54284     {
54285         
54286     },
54287     getResizeEl : function(){
54288         return this.el;
54289     }
54290 });//<script type="text/javasscript">
54291  
54292
54293 /**
54294  * @class Roo.DDView
54295  * A DnD enabled version of Roo.View.
54296  * @param {Element/String} container The Element in which to create the View.
54297  * @param {String} tpl The template string used to create the markup for each element of the View
54298  * @param {Object} config The configuration properties. These include all the config options of
54299  * {@link Roo.View} plus some specific to this class.<br>
54300  * <p>
54301  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54302  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54303  * <p>
54304  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54305 .x-view-drag-insert-above {
54306         border-top:1px dotted #3366cc;
54307 }
54308 .x-view-drag-insert-below {
54309         border-bottom:1px dotted #3366cc;
54310 }
54311 </code></pre>
54312  * 
54313  */
54314  
54315 Roo.DDView = function(container, tpl, config) {
54316     Roo.DDView.superclass.constructor.apply(this, arguments);
54317     this.getEl().setStyle("outline", "0px none");
54318     this.getEl().unselectable();
54319     if (this.dragGroup) {
54320         this.setDraggable(this.dragGroup.split(","));
54321     }
54322     if (this.dropGroup) {
54323         this.setDroppable(this.dropGroup.split(","));
54324     }
54325     if (this.deletable) {
54326         this.setDeletable();
54327     }
54328     this.isDirtyFlag = false;
54329         this.addEvents({
54330                 "drop" : true
54331         });
54332 };
54333
54334 Roo.extend(Roo.DDView, Roo.View, {
54335 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54336 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54337 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54338 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54339
54340         isFormField: true,
54341
54342         reset: Roo.emptyFn,
54343         
54344         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54345
54346         validate: function() {
54347                 return true;
54348         },
54349         
54350         destroy: function() {
54351                 this.purgeListeners();
54352                 this.getEl.removeAllListeners();
54353                 this.getEl().remove();
54354                 if (this.dragZone) {
54355                         if (this.dragZone.destroy) {
54356                                 this.dragZone.destroy();
54357                         }
54358                 }
54359                 if (this.dropZone) {
54360                         if (this.dropZone.destroy) {
54361                                 this.dropZone.destroy();
54362                         }
54363                 }
54364         },
54365
54366 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54367         getName: function() {
54368                 return this.name;
54369         },
54370
54371 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54372         setValue: function(v) {
54373                 if (!this.store) {
54374                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54375                 }
54376                 var data = {};
54377                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54378                 this.store.proxy = new Roo.data.MemoryProxy(data);
54379                 this.store.load();
54380         },
54381
54382 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54383         getValue: function() {
54384                 var result = '(';
54385                 this.store.each(function(rec) {
54386                         result += rec.id + ',';
54387                 });
54388                 return result.substr(0, result.length - 1) + ')';
54389         },
54390         
54391         getIds: function() {
54392                 var i = 0, result = new Array(this.store.getCount());
54393                 this.store.each(function(rec) {
54394                         result[i++] = rec.id;
54395                 });
54396                 return result;
54397         },
54398         
54399         isDirty: function() {
54400                 return this.isDirtyFlag;
54401         },
54402
54403 /**
54404  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54405  *      whole Element becomes the target, and this causes the drop gesture to append.
54406  */
54407     getTargetFromEvent : function(e) {
54408                 var target = e.getTarget();
54409                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54410                 target = target.parentNode;
54411                 }
54412                 if (!target) {
54413                         target = this.el.dom.lastChild || this.el.dom;
54414                 }
54415                 return target;
54416     },
54417
54418 /**
54419  *      Create the drag data which consists of an object which has the property "ddel" as
54420  *      the drag proxy element. 
54421  */
54422     getDragData : function(e) {
54423         var target = this.findItemFromChild(e.getTarget());
54424                 if(target) {
54425                         this.handleSelection(e);
54426                         var selNodes = this.getSelectedNodes();
54427             var dragData = {
54428                 source: this,
54429                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54430                 nodes: selNodes,
54431                 records: []
54432                         };
54433                         var selectedIndices = this.getSelectedIndexes();
54434                         for (var i = 0; i < selectedIndices.length; i++) {
54435                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54436                         }
54437                         if (selNodes.length == 1) {
54438                                 dragData.ddel = target.cloneNode(true); // the div element
54439                         } else {
54440                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54441                                 div.className = 'multi-proxy';
54442                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54443                                         div.appendChild(selNodes[i].cloneNode(true));
54444                                 }
54445                                 dragData.ddel = div;
54446                         }
54447             //console.log(dragData)
54448             //console.log(dragData.ddel.innerHTML)
54449                         return dragData;
54450                 }
54451         //console.log('nodragData')
54452                 return false;
54453     },
54454     
54455 /**     Specify to which ddGroup items in this DDView may be dragged. */
54456     setDraggable: function(ddGroup) {
54457         if (ddGroup instanceof Array) {
54458                 Roo.each(ddGroup, this.setDraggable, this);
54459                 return;
54460         }
54461         if (this.dragZone) {
54462                 this.dragZone.addToGroup(ddGroup);
54463         } else {
54464                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54465                                 containerScroll: true,
54466                                 ddGroup: ddGroup 
54467
54468                         });
54469 //                      Draggability implies selection. DragZone's mousedown selects the element.
54470                         if (!this.multiSelect) { this.singleSelect = true; }
54471
54472 //                      Wire the DragZone's handlers up to methods in *this*
54473                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54474                 }
54475     },
54476
54477 /**     Specify from which ddGroup this DDView accepts drops. */
54478     setDroppable: function(ddGroup) {
54479         if (ddGroup instanceof Array) {
54480                 Roo.each(ddGroup, this.setDroppable, this);
54481                 return;
54482         }
54483         if (this.dropZone) {
54484                 this.dropZone.addToGroup(ddGroup);
54485         } else {
54486                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54487                                 containerScroll: true,
54488                                 ddGroup: ddGroup
54489                         });
54490
54491 //                      Wire the DropZone's handlers up to methods in *this*
54492                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54493                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54494                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54495                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54496                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54497                 }
54498     },
54499
54500 /**     Decide whether to drop above or below a View node. */
54501     getDropPoint : function(e, n, dd){
54502         if (n == this.el.dom) { return "above"; }
54503                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54504                 var c = t + (b - t) / 2;
54505                 var y = Roo.lib.Event.getPageY(e);
54506                 if(y <= c) {
54507                         return "above";
54508                 }else{
54509                         return "below";
54510                 }
54511     },
54512
54513     onNodeEnter : function(n, dd, e, data){
54514                 return false;
54515     },
54516     
54517     onNodeOver : function(n, dd, e, data){
54518                 var pt = this.getDropPoint(e, n, dd);
54519                 // set the insert point style on the target node
54520                 var dragElClass = this.dropNotAllowed;
54521                 if (pt) {
54522                         var targetElClass;
54523                         if (pt == "above"){
54524                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54525                                 targetElClass = "x-view-drag-insert-above";
54526                         } else {
54527                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54528                                 targetElClass = "x-view-drag-insert-below";
54529                         }
54530                         if (this.lastInsertClass != targetElClass){
54531                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54532                                 this.lastInsertClass = targetElClass;
54533                         }
54534                 }
54535                 return dragElClass;
54536         },
54537
54538     onNodeOut : function(n, dd, e, data){
54539                 this.removeDropIndicators(n);
54540     },
54541
54542     onNodeDrop : function(n, dd, e, data){
54543         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54544                 return false;
54545         }
54546         var pt = this.getDropPoint(e, n, dd);
54547                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54548                 if (pt == "below") { insertAt++; }
54549                 for (var i = 0; i < data.records.length; i++) {
54550                         var r = data.records[i];
54551                         var dup = this.store.getById(r.id);
54552                         if (dup && (dd != this.dragZone)) {
54553                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54554                         } else {
54555                                 if (data.copy) {
54556                                         this.store.insert(insertAt++, r.copy());
54557                                 } else {
54558                                         data.source.isDirtyFlag = true;
54559                                         r.store.remove(r);
54560                                         this.store.insert(insertAt++, r);
54561                                 }
54562                                 this.isDirtyFlag = true;
54563                         }
54564                 }
54565                 this.dragZone.cachedTarget = null;
54566                 return true;
54567     },
54568
54569     removeDropIndicators : function(n){
54570                 if(n){
54571                         Roo.fly(n).removeClass([
54572                                 "x-view-drag-insert-above",
54573                                 "x-view-drag-insert-below"]);
54574                         this.lastInsertClass = "_noclass";
54575                 }
54576     },
54577
54578 /**
54579  *      Utility method. Add a delete option to the DDView's context menu.
54580  *      @param {String} imageUrl The URL of the "delete" icon image.
54581  */
54582         setDeletable: function(imageUrl) {
54583                 if (!this.singleSelect && !this.multiSelect) {
54584                         this.singleSelect = true;
54585                 }
54586                 var c = this.getContextMenu();
54587                 this.contextMenu.on("itemclick", function(item) {
54588                         switch (item.id) {
54589                                 case "delete":
54590                                         this.remove(this.getSelectedIndexes());
54591                                         break;
54592                         }
54593                 }, this);
54594                 this.contextMenu.add({
54595                         icon: imageUrl,
54596                         id: "delete",
54597                         text: 'Delete'
54598                 });
54599         },
54600         
54601 /**     Return the context menu for this DDView. */
54602         getContextMenu: function() {
54603                 if (!this.contextMenu) {
54604 //                      Create the View's context menu
54605                         this.contextMenu = new Roo.menu.Menu({
54606                                 id: this.id + "-contextmenu"
54607                         });
54608                         this.el.on("contextmenu", this.showContextMenu, this);
54609                 }
54610                 return this.contextMenu;
54611         },
54612         
54613         disableContextMenu: function() {
54614                 if (this.contextMenu) {
54615                         this.el.un("contextmenu", this.showContextMenu, this);
54616                 }
54617         },
54618
54619         showContextMenu: function(e, item) {
54620         item = this.findItemFromChild(e.getTarget());
54621                 if (item) {
54622                         e.stopEvent();
54623                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54624                         this.contextMenu.showAt(e.getXY());
54625             }
54626     },
54627
54628 /**
54629  *      Remove {@link Roo.data.Record}s at the specified indices.
54630  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54631  */
54632     remove: function(selectedIndices) {
54633                 selectedIndices = [].concat(selectedIndices);
54634                 for (var i = 0; i < selectedIndices.length; i++) {
54635                         var rec = this.store.getAt(selectedIndices[i]);
54636                         this.store.remove(rec);
54637                 }
54638     },
54639
54640 /**
54641  *      Double click fires the event, but also, if this is draggable, and there is only one other
54642  *      related DropZone, it transfers the selected node.
54643  */
54644     onDblClick : function(e){
54645         var item = this.findItemFromChild(e.getTarget());
54646         if(item){
54647             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54648                 return false;
54649             }
54650             if (this.dragGroup) {
54651                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54652                     while (targets.indexOf(this.dropZone) > -1) {
54653                             targets.remove(this.dropZone);
54654                                 }
54655                     if (targets.length == 1) {
54656                                         this.dragZone.cachedTarget = null;
54657                         var el = Roo.get(targets[0].getEl());
54658                         var box = el.getBox(true);
54659                         targets[0].onNodeDrop(el.dom, {
54660                                 target: el.dom,
54661                                 xy: [box.x, box.y + box.height - 1]
54662                         }, null, this.getDragData(e));
54663                     }
54664                 }
54665         }
54666     },
54667     
54668     handleSelection: function(e) {
54669                 this.dragZone.cachedTarget = null;
54670         var item = this.findItemFromChild(e.getTarget());
54671         if (!item) {
54672                 this.clearSelections(true);
54673                 return;
54674         }
54675                 if (item && (this.multiSelect || this.singleSelect)){
54676                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54677                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54678                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54679                                 this.unselect(item);
54680                         } else {
54681                                 this.select(item, this.multiSelect && e.ctrlKey);
54682                                 this.lastSelection = item;
54683                         }
54684                 }
54685     },
54686
54687     onItemClick : function(item, index, e){
54688                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54689                         return false;
54690                 }
54691                 return true;
54692     },
54693
54694     unselect : function(nodeInfo, suppressEvent){
54695                 var node = this.getNode(nodeInfo);
54696                 if(node && this.isSelected(node)){
54697                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54698                                 Roo.fly(node).removeClass(this.selectedClass);
54699                                 this.selections.remove(node);
54700                                 if(!suppressEvent){
54701                                         this.fireEvent("selectionchange", this, this.selections);
54702                                 }
54703                         }
54704                 }
54705     }
54706 });
54707 /*
54708  * Based on:
54709  * Ext JS Library 1.1.1
54710  * Copyright(c) 2006-2007, Ext JS, LLC.
54711  *
54712  * Originally Released Under LGPL - original licence link has changed is not relivant.
54713  *
54714  * Fork - LGPL
54715  * <script type="text/javascript">
54716  */
54717  
54718 /**
54719  * @class Roo.LayoutManager
54720  * @extends Roo.util.Observable
54721  * Base class for layout managers.
54722  */
54723 Roo.LayoutManager = function(container, config){
54724     Roo.LayoutManager.superclass.constructor.call(this);
54725     this.el = Roo.get(container);
54726     // ie scrollbar fix
54727     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54728         document.body.scroll = "no";
54729     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54730         this.el.position('relative');
54731     }
54732     this.id = this.el.id;
54733     this.el.addClass("x-layout-container");
54734     /** false to disable window resize monitoring @type Boolean */
54735     this.monitorWindowResize = true;
54736     this.regions = {};
54737     this.addEvents({
54738         /**
54739          * @event layout
54740          * Fires when a layout is performed. 
54741          * @param {Roo.LayoutManager} this
54742          */
54743         "layout" : true,
54744         /**
54745          * @event regionresized
54746          * Fires when the user resizes a region. 
54747          * @param {Roo.LayoutRegion} region The resized region
54748          * @param {Number} newSize The new size (width for east/west, height for north/south)
54749          */
54750         "regionresized" : true,
54751         /**
54752          * @event regioncollapsed
54753          * Fires when a region is collapsed. 
54754          * @param {Roo.LayoutRegion} region The collapsed region
54755          */
54756         "regioncollapsed" : true,
54757         /**
54758          * @event regionexpanded
54759          * Fires when a region is expanded.  
54760          * @param {Roo.LayoutRegion} region The expanded region
54761          */
54762         "regionexpanded" : true
54763     });
54764     this.updating = false;
54765     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54766 };
54767
54768 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54769     /**
54770      * Returns true if this layout is currently being updated
54771      * @return {Boolean}
54772      */
54773     isUpdating : function(){
54774         return this.updating; 
54775     },
54776     
54777     /**
54778      * Suspend the LayoutManager from doing auto-layouts while
54779      * making multiple add or remove calls
54780      */
54781     beginUpdate : function(){
54782         this.updating = true;    
54783     },
54784     
54785     /**
54786      * Restore auto-layouts and optionally disable the manager from performing a layout
54787      * @param {Boolean} noLayout true to disable a layout update 
54788      */
54789     endUpdate : function(noLayout){
54790         this.updating = false;
54791         if(!noLayout){
54792             this.layout();
54793         }    
54794     },
54795     
54796     layout: function(){
54797         
54798     },
54799     
54800     onRegionResized : function(region, newSize){
54801         this.fireEvent("regionresized", region, newSize);
54802         this.layout();
54803     },
54804     
54805     onRegionCollapsed : function(region){
54806         this.fireEvent("regioncollapsed", region);
54807     },
54808     
54809     onRegionExpanded : function(region){
54810         this.fireEvent("regionexpanded", region);
54811     },
54812         
54813     /**
54814      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54815      * performs box-model adjustments.
54816      * @return {Object} The size as an object {width: (the width), height: (the height)}
54817      */
54818     getViewSize : function(){
54819         var size;
54820         if(this.el.dom != document.body){
54821             size = this.el.getSize();
54822         }else{
54823             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54824         }
54825         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54826         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54827         return size;
54828     },
54829     
54830     /**
54831      * Returns the Element this layout is bound to.
54832      * @return {Roo.Element}
54833      */
54834     getEl : function(){
54835         return this.el;
54836     },
54837     
54838     /**
54839      * Returns the specified region.
54840      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54841      * @return {Roo.LayoutRegion}
54842      */
54843     getRegion : function(target){
54844         return this.regions[target.toLowerCase()];
54845     },
54846     
54847     onWindowResize : function(){
54848         if(this.monitorWindowResize){
54849             this.layout();
54850         }
54851     }
54852 });/*
54853  * Based on:
54854  * Ext JS Library 1.1.1
54855  * Copyright(c) 2006-2007, Ext JS, LLC.
54856  *
54857  * Originally Released Under LGPL - original licence link has changed is not relivant.
54858  *
54859  * Fork - LGPL
54860  * <script type="text/javascript">
54861  */
54862 /**
54863  * @class Roo.BorderLayout
54864  * @extends Roo.LayoutManager
54865  * @children Roo.ContentPanel
54866  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54867  * please see: <br><br>
54868  * <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>
54869  * <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>
54870  * Example:
54871  <pre><code>
54872  var layout = new Roo.BorderLayout(document.body, {
54873     north: {
54874         initialSize: 25,
54875         titlebar: false
54876     },
54877     west: {
54878         split:true,
54879         initialSize: 200,
54880         minSize: 175,
54881         maxSize: 400,
54882         titlebar: true,
54883         collapsible: true
54884     },
54885     east: {
54886         split:true,
54887         initialSize: 202,
54888         minSize: 175,
54889         maxSize: 400,
54890         titlebar: true,
54891         collapsible: true
54892     },
54893     south: {
54894         split:true,
54895         initialSize: 100,
54896         minSize: 100,
54897         maxSize: 200,
54898         titlebar: true,
54899         collapsible: true
54900     },
54901     center: {
54902         titlebar: true,
54903         autoScroll:true,
54904         resizeTabs: true,
54905         minTabWidth: 50,
54906         preferredTabWidth: 150
54907     }
54908 });
54909
54910 // shorthand
54911 var CP = Roo.ContentPanel;
54912
54913 layout.beginUpdate();
54914 layout.add("north", new CP("north", "North"));
54915 layout.add("south", new CP("south", {title: "South", closable: true}));
54916 layout.add("west", new CP("west", {title: "West"}));
54917 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54918 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54919 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54920 layout.getRegion("center").showPanel("center1");
54921 layout.endUpdate();
54922 </code></pre>
54923
54924 <b>The container the layout is rendered into can be either the body element or any other element.
54925 If it is not the body element, the container needs to either be an absolute positioned element,
54926 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54927 the container size if it is not the body element.</b>
54928
54929 * @constructor
54930 * Create a new BorderLayout
54931 * @param {String/HTMLElement/Element} container The container this layout is bound to
54932 * @param {Object} config Configuration options
54933  */
54934 Roo.BorderLayout = function(container, config){
54935     config = config || {};
54936     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54937     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54938     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54939         var target = this.factory.validRegions[i];
54940         if(config[target]){
54941             this.addRegion(target, config[target]);
54942         }
54943     }
54944 };
54945
54946 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54947         
54948         /**
54949          * @cfg {Roo.LayoutRegion} east
54950          */
54951         /**
54952          * @cfg {Roo.LayoutRegion} west
54953          */
54954         /**
54955          * @cfg {Roo.LayoutRegion} north
54956          */
54957         /**
54958          * @cfg {Roo.LayoutRegion} south
54959          */
54960         /**
54961          * @cfg {Roo.LayoutRegion} center
54962          */
54963     /**
54964      * Creates and adds a new region if it doesn't already exist.
54965      * @param {String} target The target region key (north, south, east, west or center).
54966      * @param {Object} config The regions config object
54967      * @return {BorderLayoutRegion} The new region
54968      */
54969     addRegion : function(target, config){
54970         if(!this.regions[target]){
54971             var r = this.factory.create(target, this, config);
54972             this.bindRegion(target, r);
54973         }
54974         return this.regions[target];
54975     },
54976
54977     // private (kinda)
54978     bindRegion : function(name, r){
54979         this.regions[name] = r;
54980         r.on("visibilitychange", this.layout, this);
54981         r.on("paneladded", this.layout, this);
54982         r.on("panelremoved", this.layout, this);
54983         r.on("invalidated", this.layout, this);
54984         r.on("resized", this.onRegionResized, this);
54985         r.on("collapsed", this.onRegionCollapsed, this);
54986         r.on("expanded", this.onRegionExpanded, this);
54987     },
54988
54989     /**
54990      * Performs a layout update.
54991      */
54992     layout : function(){
54993         if(this.updating) {
54994             return;
54995         }
54996         var size = this.getViewSize();
54997         var w = size.width;
54998         var h = size.height;
54999         var centerW = w;
55000         var centerH = h;
55001         var centerY = 0;
55002         var centerX = 0;
55003         //var x = 0, y = 0;
55004
55005         var rs = this.regions;
55006         var north = rs["north"];
55007         var south = rs["south"]; 
55008         var west = rs["west"];
55009         var east = rs["east"];
55010         var center = rs["center"];
55011         //if(this.hideOnLayout){ // not supported anymore
55012             //c.el.setStyle("display", "none");
55013         //}
55014         if(north && north.isVisible()){
55015             var b = north.getBox();
55016             var m = north.getMargins();
55017             b.width = w - (m.left+m.right);
55018             b.x = m.left;
55019             b.y = m.top;
55020             centerY = b.height + b.y + m.bottom;
55021             centerH -= centerY;
55022             north.updateBox(this.safeBox(b));
55023         }
55024         if(south && south.isVisible()){
55025             var b = south.getBox();
55026             var m = south.getMargins();
55027             b.width = w - (m.left+m.right);
55028             b.x = m.left;
55029             var totalHeight = (b.height + m.top + m.bottom);
55030             b.y = h - totalHeight + m.top;
55031             centerH -= totalHeight;
55032             south.updateBox(this.safeBox(b));
55033         }
55034         if(west && west.isVisible()){
55035             var b = west.getBox();
55036             var m = west.getMargins();
55037             b.height = centerH - (m.top+m.bottom);
55038             b.x = m.left;
55039             b.y = centerY + m.top;
55040             var totalWidth = (b.width + m.left + m.right);
55041             centerX += totalWidth;
55042             centerW -= totalWidth;
55043             west.updateBox(this.safeBox(b));
55044         }
55045         if(east && east.isVisible()){
55046             var b = east.getBox();
55047             var m = east.getMargins();
55048             b.height = centerH - (m.top+m.bottom);
55049             var totalWidth = (b.width + m.left + m.right);
55050             b.x = w - totalWidth + m.left;
55051             b.y = centerY + m.top;
55052             centerW -= totalWidth;
55053             east.updateBox(this.safeBox(b));
55054         }
55055         if(center){
55056             var m = center.getMargins();
55057             var centerBox = {
55058                 x: centerX + m.left,
55059                 y: centerY + m.top,
55060                 width: centerW - (m.left+m.right),
55061                 height: centerH - (m.top+m.bottom)
55062             };
55063             //if(this.hideOnLayout){
55064                 //center.el.setStyle("display", "block");
55065             //}
55066             center.updateBox(this.safeBox(centerBox));
55067         }
55068         this.el.repaint();
55069         this.fireEvent("layout", this);
55070     },
55071
55072     // private
55073     safeBox : function(box){
55074         box.width = Math.max(0, box.width);
55075         box.height = Math.max(0, box.height);
55076         return box;
55077     },
55078
55079     /**
55080      * Adds a ContentPanel (or subclass) to this layout.
55081      * @param {String} target The target region key (north, south, east, west or center).
55082      * @param {Roo.ContentPanel} panel The panel to add
55083      * @return {Roo.ContentPanel} The added panel
55084      */
55085     add : function(target, panel){
55086          
55087         target = target.toLowerCase();
55088         return this.regions[target].add(panel);
55089     },
55090
55091     /**
55092      * Remove a ContentPanel (or subclass) to this layout.
55093      * @param {String} target The target region key (north, south, east, west or center).
55094      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55095      * @return {Roo.ContentPanel} The removed panel
55096      */
55097     remove : function(target, panel){
55098         target = target.toLowerCase();
55099         return this.regions[target].remove(panel);
55100     },
55101
55102     /**
55103      * Searches all regions for a panel with the specified id
55104      * @param {String} panelId
55105      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55106      */
55107     findPanel : function(panelId){
55108         var rs = this.regions;
55109         for(var target in rs){
55110             if(typeof rs[target] != "function"){
55111                 var p = rs[target].getPanel(panelId);
55112                 if(p){
55113                     return p;
55114                 }
55115             }
55116         }
55117         return null;
55118     },
55119
55120     /**
55121      * Searches all regions for a panel with the specified id and activates (shows) it.
55122      * @param {String/ContentPanel} panelId The panels id or the panel itself
55123      * @return {Roo.ContentPanel} The shown panel or null
55124      */
55125     showPanel : function(panelId) {
55126       var rs = this.regions;
55127       for(var target in rs){
55128          var r = rs[target];
55129          if(typeof r != "function"){
55130             if(r.hasPanel(panelId)){
55131                return r.showPanel(panelId);
55132             }
55133          }
55134       }
55135       return null;
55136    },
55137
55138    /**
55139      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55140      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55141      */
55142     restoreState : function(provider){
55143         if(!provider){
55144             provider = Roo.state.Manager;
55145         }
55146         var sm = new Roo.LayoutStateManager();
55147         sm.init(this, provider);
55148     },
55149
55150     /**
55151      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55152      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55153      * a valid ContentPanel config object.  Example:
55154      * <pre><code>
55155 // Create the main layout
55156 var layout = new Roo.BorderLayout('main-ct', {
55157     west: {
55158         split:true,
55159         minSize: 175,
55160         titlebar: true
55161     },
55162     center: {
55163         title:'Components'
55164     }
55165 }, 'main-ct');
55166
55167 // Create and add multiple ContentPanels at once via configs
55168 layout.batchAdd({
55169    west: {
55170        id: 'source-files',
55171        autoCreate:true,
55172        title:'Ext Source Files',
55173        autoScroll:true,
55174        fitToFrame:true
55175    },
55176    center : {
55177        el: cview,
55178        autoScroll:true,
55179        fitToFrame:true,
55180        toolbar: tb,
55181        resizeEl:'cbody'
55182    }
55183 });
55184 </code></pre>
55185      * @param {Object} regions An object containing ContentPanel configs by region name
55186      */
55187     batchAdd : function(regions){
55188         this.beginUpdate();
55189         for(var rname in regions){
55190             var lr = this.regions[rname];
55191             if(lr){
55192                 this.addTypedPanels(lr, regions[rname]);
55193             }
55194         }
55195         this.endUpdate();
55196     },
55197
55198     // private
55199     addTypedPanels : function(lr, ps){
55200         if(typeof ps == 'string'){
55201             lr.add(new Roo.ContentPanel(ps));
55202         }
55203         else if(ps instanceof Array){
55204             for(var i =0, len = ps.length; i < len; i++){
55205                 this.addTypedPanels(lr, ps[i]);
55206             }
55207         }
55208         else if(!ps.events){ // raw config?
55209             var el = ps.el;
55210             delete ps.el; // prevent conflict
55211             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55212         }
55213         else {  // panel object assumed!
55214             lr.add(ps);
55215         }
55216     },
55217     /**
55218      * Adds a xtype elements to the layout.
55219      * <pre><code>
55220
55221 layout.addxtype({
55222        xtype : 'ContentPanel',
55223        region: 'west',
55224        items: [ .... ]
55225    }
55226 );
55227
55228 layout.addxtype({
55229         xtype : 'NestedLayoutPanel',
55230         region: 'west',
55231         layout: {
55232            center: { },
55233            west: { }   
55234         },
55235         items : [ ... list of content panels or nested layout panels.. ]
55236    }
55237 );
55238 </code></pre>
55239      * @param {Object} cfg Xtype definition of item to add.
55240      */
55241     addxtype : function(cfg)
55242     {
55243         // basically accepts a pannel...
55244         // can accept a layout region..!?!?
55245         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55246         
55247         if (!cfg.xtype.match(/Panel$/)) {
55248             return false;
55249         }
55250         var ret = false;
55251         
55252         if (typeof(cfg.region) == 'undefined') {
55253             Roo.log("Failed to add Panel, region was not set");
55254             Roo.log(cfg);
55255             return false;
55256         }
55257         var region = cfg.region;
55258         delete cfg.region;
55259         
55260           
55261         var xitems = [];
55262         if (cfg.items) {
55263             xitems = cfg.items;
55264             delete cfg.items;
55265         }
55266         var nb = false;
55267         
55268         switch(cfg.xtype) 
55269         {
55270             case 'ContentPanel':  // ContentPanel (el, cfg)
55271             case 'ScrollPanel':  // ContentPanel (el, cfg)
55272             case 'ViewPanel': 
55273                 if(cfg.autoCreate) {
55274                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55275                 } else {
55276                     var el = this.el.createChild();
55277                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55278                 }
55279                 
55280                 this.add(region, ret);
55281                 break;
55282             
55283             
55284             case 'TreePanel': // our new panel!
55285                 cfg.el = this.el.createChild();
55286                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55287                 this.add(region, ret);
55288                 break;
55289             
55290             case 'NestedLayoutPanel': 
55291                 // create a new Layout (which is  a Border Layout...
55292                 var el = this.el.createChild();
55293                 var clayout = cfg.layout;
55294                 delete cfg.layout;
55295                 clayout.items   = clayout.items  || [];
55296                 // replace this exitems with the clayout ones..
55297                 xitems = clayout.items;
55298                  
55299                 
55300                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55301                     cfg.background = false;
55302                 }
55303                 var layout = new Roo.BorderLayout(el, clayout);
55304                 
55305                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55306                 //console.log('adding nested layout panel '  + cfg.toSource());
55307                 this.add(region, ret);
55308                 nb = {}; /// find first...
55309                 break;
55310                 
55311             case 'GridPanel': 
55312             
55313                 // needs grid and region
55314                 
55315                 //var el = this.getRegion(region).el.createChild();
55316                 var el = this.el.createChild();
55317                 // create the grid first...
55318                 
55319                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55320                 delete cfg.grid;
55321                 if (region == 'center' && this.active ) {
55322                     cfg.background = false;
55323                 }
55324                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55325                 
55326                 this.add(region, ret);
55327                 if (cfg.background) {
55328                     ret.on('activate', function(gp) {
55329                         if (!gp.grid.rendered) {
55330                             gp.grid.render();
55331                         }
55332                     });
55333                 } else {
55334                     grid.render();
55335                 }
55336                 break;
55337            
55338            
55339            
55340                 
55341                 
55342                 
55343             default:
55344                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55345                     
55346                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55347                     this.add(region, ret);
55348                 } else {
55349                 
55350                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55351                     return null;
55352                 }
55353                 
55354              // GridPanel (grid, cfg)
55355             
55356         }
55357         this.beginUpdate();
55358         // add children..
55359         var region = '';
55360         var abn = {};
55361         Roo.each(xitems, function(i)  {
55362             region = nb && i.region ? i.region : false;
55363             
55364             var add = ret.addxtype(i);
55365            
55366             if (region) {
55367                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55368                 if (!i.background) {
55369                     abn[region] = nb[region] ;
55370                 }
55371             }
55372             
55373         });
55374         this.endUpdate();
55375
55376         // make the last non-background panel active..
55377         //if (nb) { Roo.log(abn); }
55378         if (nb) {
55379             
55380             for(var r in abn) {
55381                 region = this.getRegion(r);
55382                 if (region) {
55383                     // tried using nb[r], but it does not work..
55384                      
55385                     region.showPanel(abn[r]);
55386                    
55387                 }
55388             }
55389         }
55390         return ret;
55391         
55392     }
55393 });
55394
55395 /**
55396  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55397  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55398  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55399  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55400  * <pre><code>
55401 // shorthand
55402 var CP = Roo.ContentPanel;
55403
55404 var layout = Roo.BorderLayout.create({
55405     north: {
55406         initialSize: 25,
55407         titlebar: false,
55408         panels: [new CP("north", "North")]
55409     },
55410     west: {
55411         split:true,
55412         initialSize: 200,
55413         minSize: 175,
55414         maxSize: 400,
55415         titlebar: true,
55416         collapsible: true,
55417         panels: [new CP("west", {title: "West"})]
55418     },
55419     east: {
55420         split:true,
55421         initialSize: 202,
55422         minSize: 175,
55423         maxSize: 400,
55424         titlebar: true,
55425         collapsible: true,
55426         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55427     },
55428     south: {
55429         split:true,
55430         initialSize: 100,
55431         minSize: 100,
55432         maxSize: 200,
55433         titlebar: true,
55434         collapsible: true,
55435         panels: [new CP("south", {title: "South", closable: true})]
55436     },
55437     center: {
55438         titlebar: true,
55439         autoScroll:true,
55440         resizeTabs: true,
55441         minTabWidth: 50,
55442         preferredTabWidth: 150,
55443         panels: [
55444             new CP("center1", {title: "Close Me", closable: true}),
55445             new CP("center2", {title: "Center Panel", closable: false})
55446         ]
55447     }
55448 }, document.body);
55449
55450 layout.getRegion("center").showPanel("center1");
55451 </code></pre>
55452  * @param config
55453  * @param targetEl
55454  */
55455 Roo.BorderLayout.create = function(config, targetEl){
55456     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55457     layout.beginUpdate();
55458     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55459     for(var j = 0, jlen = regions.length; j < jlen; j++){
55460         var lr = regions[j];
55461         if(layout.regions[lr] && config[lr].panels){
55462             var r = layout.regions[lr];
55463             var ps = config[lr].panels;
55464             layout.addTypedPanels(r, ps);
55465         }
55466     }
55467     layout.endUpdate();
55468     return layout;
55469 };
55470
55471 // private
55472 Roo.BorderLayout.RegionFactory = {
55473     // private
55474     validRegions : ["north","south","east","west","center"],
55475
55476     // private
55477     create : function(target, mgr, config){
55478         target = target.toLowerCase();
55479         if(config.lightweight || config.basic){
55480             return new Roo.BasicLayoutRegion(mgr, config, target);
55481         }
55482         switch(target){
55483             case "north":
55484                 return new Roo.NorthLayoutRegion(mgr, config);
55485             case "south":
55486                 return new Roo.SouthLayoutRegion(mgr, config);
55487             case "east":
55488                 return new Roo.EastLayoutRegion(mgr, config);
55489             case "west":
55490                 return new Roo.WestLayoutRegion(mgr, config);
55491             case "center":
55492                 return new Roo.CenterLayoutRegion(mgr, config);
55493         }
55494         throw 'Layout region "'+target+'" not supported.';
55495     }
55496 };/*
55497  * Based on:
55498  * Ext JS Library 1.1.1
55499  * Copyright(c) 2006-2007, Ext JS, LLC.
55500  *
55501  * Originally Released Under LGPL - original licence link has changed is not relivant.
55502  *
55503  * Fork - LGPL
55504  * <script type="text/javascript">
55505  */
55506  
55507 /**
55508  * @class Roo.BasicLayoutRegion
55509  * @extends Roo.util.Observable
55510  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55511  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55512  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55513  */
55514 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55515     this.mgr = mgr;
55516     this.position  = pos;
55517     this.events = {
55518         /**
55519          * @scope Roo.BasicLayoutRegion
55520          */
55521         
55522         /**
55523          * @event beforeremove
55524          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55525          * @param {Roo.LayoutRegion} this
55526          * @param {Roo.ContentPanel} panel The panel
55527          * @param {Object} e The cancel event object
55528          */
55529         "beforeremove" : true,
55530         /**
55531          * @event invalidated
55532          * Fires when the layout for this region is changed.
55533          * @param {Roo.LayoutRegion} this
55534          */
55535         "invalidated" : true,
55536         /**
55537          * @event visibilitychange
55538          * Fires when this region is shown or hidden 
55539          * @param {Roo.LayoutRegion} this
55540          * @param {Boolean} visibility true or false
55541          */
55542         "visibilitychange" : true,
55543         /**
55544          * @event paneladded
55545          * Fires when a panel is added. 
55546          * @param {Roo.LayoutRegion} this
55547          * @param {Roo.ContentPanel} panel The panel
55548          */
55549         "paneladded" : true,
55550         /**
55551          * @event panelremoved
55552          * Fires when a panel is removed. 
55553          * @param {Roo.LayoutRegion} this
55554          * @param {Roo.ContentPanel} panel The panel
55555          */
55556         "panelremoved" : true,
55557         /**
55558          * @event beforecollapse
55559          * Fires when this region before collapse.
55560          * @param {Roo.LayoutRegion} this
55561          */
55562         "beforecollapse" : true,
55563         /**
55564          * @event collapsed
55565          * Fires when this region is collapsed.
55566          * @param {Roo.LayoutRegion} this
55567          */
55568         "collapsed" : true,
55569         /**
55570          * @event expanded
55571          * Fires when this region is expanded.
55572          * @param {Roo.LayoutRegion} this
55573          */
55574         "expanded" : true,
55575         /**
55576          * @event slideshow
55577          * Fires when this region is slid into view.
55578          * @param {Roo.LayoutRegion} this
55579          */
55580         "slideshow" : true,
55581         /**
55582          * @event slidehide
55583          * Fires when this region slides out of view. 
55584          * @param {Roo.LayoutRegion} this
55585          */
55586         "slidehide" : true,
55587         /**
55588          * @event panelactivated
55589          * Fires when a panel is activated. 
55590          * @param {Roo.LayoutRegion} this
55591          * @param {Roo.ContentPanel} panel The activated panel
55592          */
55593         "panelactivated" : true,
55594         /**
55595          * @event resized
55596          * Fires when the user resizes this region. 
55597          * @param {Roo.LayoutRegion} this
55598          * @param {Number} newSize The new size (width for east/west, height for north/south)
55599          */
55600         "resized" : true
55601     };
55602     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55603     this.panels = new Roo.util.MixedCollection();
55604     this.panels.getKey = this.getPanelId.createDelegate(this);
55605     this.box = null;
55606     this.activePanel = null;
55607     // ensure listeners are added...
55608     
55609     if (config.listeners || config.events) {
55610         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55611             listeners : config.listeners || {},
55612             events : config.events || {}
55613         });
55614     }
55615     
55616     if(skipConfig !== true){
55617         this.applyConfig(config);
55618     }
55619 };
55620
55621 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55622     getPanelId : function(p){
55623         return p.getId();
55624     },
55625     
55626     applyConfig : function(config){
55627         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55628         this.config = config;
55629         
55630     },
55631     
55632     /**
55633      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55634      * the width, for horizontal (north, south) the height.
55635      * @param {Number} newSize The new width or height
55636      */
55637     resizeTo : function(newSize){
55638         var el = this.el ? this.el :
55639                  (this.activePanel ? this.activePanel.getEl() : null);
55640         if(el){
55641             switch(this.position){
55642                 case "east":
55643                 case "west":
55644                     el.setWidth(newSize);
55645                     this.fireEvent("resized", this, newSize);
55646                 break;
55647                 case "north":
55648                 case "south":
55649                     el.setHeight(newSize);
55650                     this.fireEvent("resized", this, newSize);
55651                 break;                
55652             }
55653         }
55654     },
55655     
55656     getBox : function(){
55657         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55658     },
55659     
55660     getMargins : function(){
55661         return this.margins;
55662     },
55663     
55664     updateBox : function(box){
55665         this.box = box;
55666         var el = this.activePanel.getEl();
55667         el.dom.style.left = box.x + "px";
55668         el.dom.style.top = box.y + "px";
55669         this.activePanel.setSize(box.width, box.height);
55670     },
55671     
55672     /**
55673      * Returns the container element for this region.
55674      * @return {Roo.Element}
55675      */
55676     getEl : function(){
55677         return this.activePanel;
55678     },
55679     
55680     /**
55681      * Returns true if this region is currently visible.
55682      * @return {Boolean}
55683      */
55684     isVisible : function(){
55685         return this.activePanel ? true : false;
55686     },
55687     
55688     setActivePanel : function(panel){
55689         panel = this.getPanel(panel);
55690         if(this.activePanel && this.activePanel != panel){
55691             this.activePanel.setActiveState(false);
55692             this.activePanel.getEl().setLeftTop(-10000,-10000);
55693         }
55694         this.activePanel = panel;
55695         panel.setActiveState(true);
55696         if(this.box){
55697             panel.setSize(this.box.width, this.box.height);
55698         }
55699         this.fireEvent("panelactivated", this, panel);
55700         this.fireEvent("invalidated");
55701     },
55702     
55703     /**
55704      * Show the specified panel.
55705      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55706      * @return {Roo.ContentPanel} The shown panel or null
55707      */
55708     showPanel : function(panel){
55709         if(panel = this.getPanel(panel)){
55710             this.setActivePanel(panel);
55711         }
55712         return panel;
55713     },
55714     
55715     /**
55716      * Get the active panel for this region.
55717      * @return {Roo.ContentPanel} The active panel or null
55718      */
55719     getActivePanel : function(){
55720         return this.activePanel;
55721     },
55722     
55723     /**
55724      * Add the passed ContentPanel(s)
55725      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55726      * @return {Roo.ContentPanel} The panel added (if only one was added)
55727      */
55728     add : function(panel){
55729         if(arguments.length > 1){
55730             for(var i = 0, len = arguments.length; i < len; i++) {
55731                 this.add(arguments[i]);
55732             }
55733             return null;
55734         }
55735         if(this.hasPanel(panel)){
55736             this.showPanel(panel);
55737             return panel;
55738         }
55739         var el = panel.getEl();
55740         if(el.dom.parentNode != this.mgr.el.dom){
55741             this.mgr.el.dom.appendChild(el.dom);
55742         }
55743         if(panel.setRegion){
55744             panel.setRegion(this);
55745         }
55746         this.panels.add(panel);
55747         el.setStyle("position", "absolute");
55748         if(!panel.background){
55749             this.setActivePanel(panel);
55750             if(this.config.initialSize && this.panels.getCount()==1){
55751                 this.resizeTo(this.config.initialSize);
55752             }
55753         }
55754         this.fireEvent("paneladded", this, panel);
55755         return panel;
55756     },
55757     
55758     /**
55759      * Returns true if the panel is in this region.
55760      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55761      * @return {Boolean}
55762      */
55763     hasPanel : function(panel){
55764         if(typeof panel == "object"){ // must be panel obj
55765             panel = panel.getId();
55766         }
55767         return this.getPanel(panel) ? true : false;
55768     },
55769     
55770     /**
55771      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55772      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55773      * @param {Boolean} preservePanel Overrides the config preservePanel option
55774      * @return {Roo.ContentPanel} The panel that was removed
55775      */
55776     remove : function(panel, preservePanel){
55777         panel = this.getPanel(panel);
55778         if(!panel){
55779             return null;
55780         }
55781         var e = {};
55782         this.fireEvent("beforeremove", this, panel, e);
55783         if(e.cancel === true){
55784             return null;
55785         }
55786         var panelId = panel.getId();
55787         this.panels.removeKey(panelId);
55788         return panel;
55789     },
55790     
55791     /**
55792      * Returns the panel specified or null if it's not in this region.
55793      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55794      * @return {Roo.ContentPanel}
55795      */
55796     getPanel : function(id){
55797         if(typeof id == "object"){ // must be panel obj
55798             return id;
55799         }
55800         return this.panels.get(id);
55801     },
55802     
55803     /**
55804      * Returns this regions position (north/south/east/west/center).
55805      * @return {String} 
55806      */
55807     getPosition: function(){
55808         return this.position;    
55809     }
55810 });/*
55811  * Based on:
55812  * Ext JS Library 1.1.1
55813  * Copyright(c) 2006-2007, Ext JS, LLC.
55814  *
55815  * Originally Released Under LGPL - original licence link has changed is not relivant.
55816  *
55817  * Fork - LGPL
55818  * <script type="text/javascript">
55819  */
55820  
55821 /**
55822  * @class Roo.LayoutRegion
55823  * @extends Roo.BasicLayoutRegion
55824  * This class represents a region in a layout manager.
55825  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55826  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55827  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55828  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55829  * @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})
55830  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55831  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55832  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55833  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55834  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55835  * @cfg {String}    title           The title for the region (overrides panel titles)
55836  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55837  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55838  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55839  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55840  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55841  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55842  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55843  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55844  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55845  * @cfg {Boolean}   showPin         True to show a pin button
55846  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55847  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55848  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55849  * @cfg {Number}    width           For East/West panels
55850  * @cfg {Number}    height          For North/South panels
55851  * @cfg {Boolean}   split           To show the splitter
55852  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55853  */
55854 Roo.LayoutRegion = function(mgr, config, pos){
55855     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55856     var dh = Roo.DomHelper;
55857     /** This region's container element 
55858     * @type Roo.Element */
55859     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55860     /** This region's title element 
55861     * @type Roo.Element */
55862
55863     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55864         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55865         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55866     ]}, true);
55867     this.titleEl.enableDisplayMode();
55868     /** This region's title text element 
55869     * @type HTMLElement */
55870     this.titleTextEl = this.titleEl.dom.firstChild;
55871     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55872     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55873     this.closeBtn.enableDisplayMode();
55874     this.closeBtn.on("click", this.closeClicked, this);
55875     this.closeBtn.hide();
55876
55877     this.createBody(config);
55878     this.visible = true;
55879     this.collapsed = false;
55880
55881     if(config.hideWhenEmpty){
55882         this.hide();
55883         this.on("paneladded", this.validateVisibility, this);
55884         this.on("panelremoved", this.validateVisibility, this);
55885     }
55886     this.applyConfig(config);
55887 };
55888
55889 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55890
55891     createBody : function(){
55892         /** This region's body element 
55893         * @type Roo.Element */
55894         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55895     },
55896
55897     applyConfig : function(c){
55898         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55899             var dh = Roo.DomHelper;
55900             if(c.titlebar !== false){
55901                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55902                 this.collapseBtn.on("click", this.collapse, this);
55903                 this.collapseBtn.enableDisplayMode();
55904
55905                 if(c.showPin === true || this.showPin){
55906                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55907                     this.stickBtn.enableDisplayMode();
55908                     this.stickBtn.on("click", this.expand, this);
55909                     this.stickBtn.hide();
55910                 }
55911             }
55912             /** This region's collapsed element
55913             * @type Roo.Element */
55914             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55915                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55916             ]}, true);
55917             if(c.floatable !== false){
55918                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55919                this.collapsedEl.on("click", this.collapseClick, this);
55920             }
55921
55922             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55923                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55924                    id: "message", unselectable: "on", style:{"float":"left"}});
55925                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55926              }
55927             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55928             this.expandBtn.on("click", this.expand, this);
55929         }
55930         if(this.collapseBtn){
55931             this.collapseBtn.setVisible(c.collapsible == true);
55932         }
55933         this.cmargins = c.cmargins || this.cmargins ||
55934                          (this.position == "west" || this.position == "east" ?
55935                              {top: 0, left: 2, right:2, bottom: 0} :
55936                              {top: 2, left: 0, right:0, bottom: 2});
55937         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55938         this.bottomTabs = c.tabPosition != "top";
55939         this.autoScroll = c.autoScroll || false;
55940         if(this.autoScroll){
55941             this.bodyEl.setStyle("overflow", "auto");
55942         }else{
55943             this.bodyEl.setStyle("overflow", "hidden");
55944         }
55945         //if(c.titlebar !== false){
55946             if((!c.titlebar && !c.title) || c.titlebar === false){
55947                 this.titleEl.hide();
55948             }else{
55949                 this.titleEl.show();
55950                 if(c.title){
55951                     this.titleTextEl.innerHTML = c.title;
55952                 }
55953             }
55954         //}
55955         this.duration = c.duration || .30;
55956         this.slideDuration = c.slideDuration || .45;
55957         this.config = c;
55958         if(c.collapsed){
55959             this.collapse(true);
55960         }
55961         if(c.hidden){
55962             this.hide();
55963         }
55964     },
55965     /**
55966      * Returns true if this region is currently visible.
55967      * @return {Boolean}
55968      */
55969     isVisible : function(){
55970         return this.visible;
55971     },
55972
55973     /**
55974      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55975      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55976      */
55977     setCollapsedTitle : function(title){
55978         title = title || "&#160;";
55979         if(this.collapsedTitleTextEl){
55980             this.collapsedTitleTextEl.innerHTML = title;
55981         }
55982     },
55983
55984     getBox : function(){
55985         var b;
55986         if(!this.collapsed){
55987             b = this.el.getBox(false, true);
55988         }else{
55989             b = this.collapsedEl.getBox(false, true);
55990         }
55991         return b;
55992     },
55993
55994     getMargins : function(){
55995         return this.collapsed ? this.cmargins : this.margins;
55996     },
55997
55998     highlight : function(){
55999         this.el.addClass("x-layout-panel-dragover");
56000     },
56001
56002     unhighlight : function(){
56003         this.el.removeClass("x-layout-panel-dragover");
56004     },
56005
56006     updateBox : function(box){
56007         this.box = box;
56008         if(!this.collapsed){
56009             this.el.dom.style.left = box.x + "px";
56010             this.el.dom.style.top = box.y + "px";
56011             this.updateBody(box.width, box.height);
56012         }else{
56013             this.collapsedEl.dom.style.left = box.x + "px";
56014             this.collapsedEl.dom.style.top = box.y + "px";
56015             this.collapsedEl.setSize(box.width, box.height);
56016         }
56017         if(this.tabs){
56018             this.tabs.autoSizeTabs();
56019         }
56020     },
56021
56022     updateBody : function(w, h){
56023         if(w !== null){
56024             this.el.setWidth(w);
56025             w -= this.el.getBorderWidth("rl");
56026             if(this.config.adjustments){
56027                 w += this.config.adjustments[0];
56028             }
56029         }
56030         if(h !== null){
56031             this.el.setHeight(h);
56032             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56033             h -= this.el.getBorderWidth("tb");
56034             if(this.config.adjustments){
56035                 h += this.config.adjustments[1];
56036             }
56037             this.bodyEl.setHeight(h);
56038             if(this.tabs){
56039                 h = this.tabs.syncHeight(h);
56040             }
56041         }
56042         if(this.panelSize){
56043             w = w !== null ? w : this.panelSize.width;
56044             h = h !== null ? h : this.panelSize.height;
56045         }
56046         if(this.activePanel){
56047             var el = this.activePanel.getEl();
56048             w = w !== null ? w : el.getWidth();
56049             h = h !== null ? h : el.getHeight();
56050             this.panelSize = {width: w, height: h};
56051             this.activePanel.setSize(w, h);
56052         }
56053         if(Roo.isIE && this.tabs){
56054             this.tabs.el.repaint();
56055         }
56056     },
56057
56058     /**
56059      * Returns the container element for this region.
56060      * @return {Roo.Element}
56061      */
56062     getEl : function(){
56063         return this.el;
56064     },
56065
56066     /**
56067      * Hides this region.
56068      */
56069     hide : function(){
56070         if(!this.collapsed){
56071             this.el.dom.style.left = "-2000px";
56072             this.el.hide();
56073         }else{
56074             this.collapsedEl.dom.style.left = "-2000px";
56075             this.collapsedEl.hide();
56076         }
56077         this.visible = false;
56078         this.fireEvent("visibilitychange", this, false);
56079     },
56080
56081     /**
56082      * Shows this region if it was previously hidden.
56083      */
56084     show : function(){
56085         if(!this.collapsed){
56086             this.el.show();
56087         }else{
56088             this.collapsedEl.show();
56089         }
56090         this.visible = true;
56091         this.fireEvent("visibilitychange", this, true);
56092     },
56093
56094     closeClicked : function(){
56095         if(this.activePanel){
56096             this.remove(this.activePanel);
56097         }
56098     },
56099
56100     collapseClick : function(e){
56101         if(this.isSlid){
56102            e.stopPropagation();
56103            this.slideIn();
56104         }else{
56105            e.stopPropagation();
56106            this.slideOut();
56107         }
56108     },
56109
56110     /**
56111      * Collapses this region.
56112      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56113      */
56114     collapse : function(skipAnim, skipCheck){
56115         if(this.collapsed) {
56116             return;
56117         }
56118         
56119         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56120             
56121             this.collapsed = true;
56122             if(this.split){
56123                 this.split.el.hide();
56124             }
56125             if(this.config.animate && skipAnim !== true){
56126                 this.fireEvent("invalidated", this);
56127                 this.animateCollapse();
56128             }else{
56129                 this.el.setLocation(-20000,-20000);
56130                 this.el.hide();
56131                 this.collapsedEl.show();
56132                 this.fireEvent("collapsed", this);
56133                 this.fireEvent("invalidated", this);
56134             }
56135         }
56136         
56137     },
56138
56139     animateCollapse : function(){
56140         // overridden
56141     },
56142
56143     /**
56144      * Expands this region if it was previously collapsed.
56145      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56146      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56147      */
56148     expand : function(e, skipAnim){
56149         if(e) {
56150             e.stopPropagation();
56151         }
56152         if(!this.collapsed || this.el.hasActiveFx()) {
56153             return;
56154         }
56155         if(this.isSlid){
56156             this.afterSlideIn();
56157             skipAnim = true;
56158         }
56159         this.collapsed = false;
56160         if(this.config.animate && skipAnim !== true){
56161             this.animateExpand();
56162         }else{
56163             this.el.show();
56164             if(this.split){
56165                 this.split.el.show();
56166             }
56167             this.collapsedEl.setLocation(-2000,-2000);
56168             this.collapsedEl.hide();
56169             this.fireEvent("invalidated", this);
56170             this.fireEvent("expanded", this);
56171         }
56172     },
56173
56174     animateExpand : function(){
56175         // overridden
56176     },
56177
56178     initTabs : function()
56179     {
56180         this.bodyEl.setStyle("overflow", "hidden");
56181         var ts = new Roo.TabPanel(
56182                 this.bodyEl.dom,
56183                 {
56184                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56185                     disableTooltips: this.config.disableTabTips,
56186                     toolbar : this.config.toolbar
56187                 }
56188         );
56189         if(this.config.hideTabs){
56190             ts.stripWrap.setDisplayed(false);
56191         }
56192         this.tabs = ts;
56193         ts.resizeTabs = this.config.resizeTabs === true;
56194         ts.minTabWidth = this.config.minTabWidth || 40;
56195         ts.maxTabWidth = this.config.maxTabWidth || 250;
56196         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56197         ts.monitorResize = false;
56198         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56199         ts.bodyEl.addClass('x-layout-tabs-body');
56200         this.panels.each(this.initPanelAsTab, this);
56201     },
56202
56203     initPanelAsTab : function(panel){
56204         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56205                     this.config.closeOnTab && panel.isClosable());
56206         if(panel.tabTip !== undefined){
56207             ti.setTooltip(panel.tabTip);
56208         }
56209         ti.on("activate", function(){
56210               this.setActivePanel(panel);
56211         }, this);
56212         if(this.config.closeOnTab){
56213             ti.on("beforeclose", function(t, e){
56214                 e.cancel = true;
56215                 this.remove(panel);
56216             }, this);
56217         }
56218         return ti;
56219     },
56220
56221     updatePanelTitle : function(panel, title){
56222         if(this.activePanel == panel){
56223             this.updateTitle(title);
56224         }
56225         if(this.tabs){
56226             var ti = this.tabs.getTab(panel.getEl().id);
56227             ti.setText(title);
56228             if(panel.tabTip !== undefined){
56229                 ti.setTooltip(panel.tabTip);
56230             }
56231         }
56232     },
56233
56234     updateTitle : function(title){
56235         if(this.titleTextEl && !this.config.title){
56236             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56237         }
56238     },
56239
56240     setActivePanel : function(panel){
56241         panel = this.getPanel(panel);
56242         if(this.activePanel && this.activePanel != panel){
56243             this.activePanel.setActiveState(false);
56244         }
56245         this.activePanel = panel;
56246         panel.setActiveState(true);
56247         if(this.panelSize){
56248             panel.setSize(this.panelSize.width, this.panelSize.height);
56249         }
56250         if(this.closeBtn){
56251             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56252         }
56253         this.updateTitle(panel.getTitle());
56254         if(this.tabs){
56255             this.fireEvent("invalidated", this);
56256         }
56257         this.fireEvent("panelactivated", this, panel);
56258     },
56259
56260     /**
56261      * Shows the specified panel.
56262      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56263      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56264      */
56265     showPanel : function(panel)
56266     {
56267         panel = this.getPanel(panel);
56268         if(panel){
56269             if(this.tabs){
56270                 var tab = this.tabs.getTab(panel.getEl().id);
56271                 if(tab.isHidden()){
56272                     this.tabs.unhideTab(tab.id);
56273                 }
56274                 tab.activate();
56275             }else{
56276                 this.setActivePanel(panel);
56277             }
56278         }
56279         return panel;
56280     },
56281
56282     /**
56283      * Get the active panel for this region.
56284      * @return {Roo.ContentPanel} The active panel or null
56285      */
56286     getActivePanel : function(){
56287         return this.activePanel;
56288     },
56289
56290     validateVisibility : function(){
56291         if(this.panels.getCount() < 1){
56292             this.updateTitle("&#160;");
56293             this.closeBtn.hide();
56294             this.hide();
56295         }else{
56296             if(!this.isVisible()){
56297                 this.show();
56298             }
56299         }
56300     },
56301
56302     /**
56303      * Adds the passed ContentPanel(s) to this region.
56304      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56305      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56306      */
56307     add : function(panel){
56308         if(arguments.length > 1){
56309             for(var i = 0, len = arguments.length; i < len; i++) {
56310                 this.add(arguments[i]);
56311             }
56312             return null;
56313         }
56314         if(this.hasPanel(panel)){
56315             this.showPanel(panel);
56316             return panel;
56317         }
56318         panel.setRegion(this);
56319         this.panels.add(panel);
56320         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56321             this.bodyEl.dom.appendChild(panel.getEl().dom);
56322             if(panel.background !== true){
56323                 this.setActivePanel(panel);
56324             }
56325             this.fireEvent("paneladded", this, panel);
56326             return panel;
56327         }
56328         if(!this.tabs){
56329             this.initTabs();
56330         }else{
56331             this.initPanelAsTab(panel);
56332         }
56333         if(panel.background !== true){
56334             this.tabs.activate(panel.getEl().id);
56335         }
56336         this.fireEvent("paneladded", this, panel);
56337         return panel;
56338     },
56339
56340     /**
56341      * Hides the tab for the specified panel.
56342      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56343      */
56344     hidePanel : function(panel){
56345         if(this.tabs && (panel = this.getPanel(panel))){
56346             this.tabs.hideTab(panel.getEl().id);
56347         }
56348     },
56349
56350     /**
56351      * Unhides the tab for a previously hidden panel.
56352      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56353      */
56354     unhidePanel : function(panel){
56355         if(this.tabs && (panel = this.getPanel(panel))){
56356             this.tabs.unhideTab(panel.getEl().id);
56357         }
56358     },
56359
56360     clearPanels : function(){
56361         while(this.panels.getCount() > 0){
56362              this.remove(this.panels.first());
56363         }
56364     },
56365
56366     /**
56367      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56368      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56369      * @param {Boolean} preservePanel Overrides the config preservePanel option
56370      * @return {Roo.ContentPanel} The panel that was removed
56371      */
56372     remove : function(panel, preservePanel){
56373         panel = this.getPanel(panel);
56374         if(!panel){
56375             return null;
56376         }
56377         var e = {};
56378         this.fireEvent("beforeremove", this, panel, e);
56379         if(e.cancel === true){
56380             return null;
56381         }
56382         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56383         var panelId = panel.getId();
56384         this.panels.removeKey(panelId);
56385         if(preservePanel){
56386             document.body.appendChild(panel.getEl().dom);
56387         }
56388         if(this.tabs){
56389             this.tabs.removeTab(panel.getEl().id);
56390         }else if (!preservePanel){
56391             this.bodyEl.dom.removeChild(panel.getEl().dom);
56392         }
56393         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56394             var p = this.panels.first();
56395             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56396             tempEl.appendChild(p.getEl().dom);
56397             this.bodyEl.update("");
56398             this.bodyEl.dom.appendChild(p.getEl().dom);
56399             tempEl = null;
56400             this.updateTitle(p.getTitle());
56401             this.tabs = null;
56402             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56403             this.setActivePanel(p);
56404         }
56405         panel.setRegion(null);
56406         if(this.activePanel == panel){
56407             this.activePanel = null;
56408         }
56409         if(this.config.autoDestroy !== false && preservePanel !== true){
56410             try{panel.destroy();}catch(e){}
56411         }
56412         this.fireEvent("panelremoved", this, panel);
56413         return panel;
56414     },
56415
56416     /**
56417      * Returns the TabPanel component used by this region
56418      * @return {Roo.TabPanel}
56419      */
56420     getTabs : function(){
56421         return this.tabs;
56422     },
56423
56424     createTool : function(parentEl, className){
56425         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56426             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56427         btn.addClassOnOver("x-layout-tools-button-over");
56428         return btn;
56429     }
56430 });/*
56431  * Based on:
56432  * Ext JS Library 1.1.1
56433  * Copyright(c) 2006-2007, Ext JS, LLC.
56434  *
56435  * Originally Released Under LGPL - original licence link has changed is not relivant.
56436  *
56437  * Fork - LGPL
56438  * <script type="text/javascript">
56439  */
56440  
56441
56442
56443 /**
56444  * @class Roo.SplitLayoutRegion
56445  * @extends Roo.LayoutRegion
56446  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56447  */
56448 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56449     this.cursor = cursor;
56450     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56451 };
56452
56453 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56454     splitTip : "Drag to resize.",
56455     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56456     useSplitTips : false,
56457
56458     applyConfig : function(config){
56459         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56460         if(config.split){
56461             if(!this.split){
56462                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56463                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56464                 /** The SplitBar for this region 
56465                 * @type Roo.SplitBar */
56466                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56467                 this.split.on("moved", this.onSplitMove, this);
56468                 this.split.useShim = config.useShim === true;
56469                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56470                 if(this.useSplitTips){
56471                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56472                 }
56473                 if(config.collapsible){
56474                     this.split.el.on("dblclick", this.collapse,  this);
56475                 }
56476             }
56477             if(typeof config.minSize != "undefined"){
56478                 this.split.minSize = config.minSize;
56479             }
56480             if(typeof config.maxSize != "undefined"){
56481                 this.split.maxSize = config.maxSize;
56482             }
56483             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56484                 this.hideSplitter();
56485             }
56486         }
56487     },
56488
56489     getHMaxSize : function(){
56490          var cmax = this.config.maxSize || 10000;
56491          var center = this.mgr.getRegion("center");
56492          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56493     },
56494
56495     getVMaxSize : function(){
56496          var cmax = this.config.maxSize || 10000;
56497          var center = this.mgr.getRegion("center");
56498          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56499     },
56500
56501     onSplitMove : function(split, newSize){
56502         this.fireEvent("resized", this, newSize);
56503     },
56504     
56505     /** 
56506      * Returns the {@link Roo.SplitBar} for this region.
56507      * @return {Roo.SplitBar}
56508      */
56509     getSplitBar : function(){
56510         return this.split;
56511     },
56512     
56513     hide : function(){
56514         this.hideSplitter();
56515         Roo.SplitLayoutRegion.superclass.hide.call(this);
56516     },
56517
56518     hideSplitter : function(){
56519         if(this.split){
56520             this.split.el.setLocation(-2000,-2000);
56521             this.split.el.hide();
56522         }
56523     },
56524
56525     show : function(){
56526         if(this.split){
56527             this.split.el.show();
56528         }
56529         Roo.SplitLayoutRegion.superclass.show.call(this);
56530     },
56531     
56532     beforeSlide: function(){
56533         if(Roo.isGecko){// firefox overflow auto bug workaround
56534             this.bodyEl.clip();
56535             if(this.tabs) {
56536                 this.tabs.bodyEl.clip();
56537             }
56538             if(this.activePanel){
56539                 this.activePanel.getEl().clip();
56540                 
56541                 if(this.activePanel.beforeSlide){
56542                     this.activePanel.beforeSlide();
56543                 }
56544             }
56545         }
56546     },
56547     
56548     afterSlide : function(){
56549         if(Roo.isGecko){// firefox overflow auto bug workaround
56550             this.bodyEl.unclip();
56551             if(this.tabs) {
56552                 this.tabs.bodyEl.unclip();
56553             }
56554             if(this.activePanel){
56555                 this.activePanel.getEl().unclip();
56556                 if(this.activePanel.afterSlide){
56557                     this.activePanel.afterSlide();
56558                 }
56559             }
56560         }
56561     },
56562
56563     initAutoHide : function(){
56564         if(this.autoHide !== false){
56565             if(!this.autoHideHd){
56566                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56567                 this.autoHideHd = {
56568                     "mouseout": function(e){
56569                         if(!e.within(this.el, true)){
56570                             st.delay(500);
56571                         }
56572                     },
56573                     "mouseover" : function(e){
56574                         st.cancel();
56575                     },
56576                     scope : this
56577                 };
56578             }
56579             this.el.on(this.autoHideHd);
56580         }
56581     },
56582
56583     clearAutoHide : function(){
56584         if(this.autoHide !== false){
56585             this.el.un("mouseout", this.autoHideHd.mouseout);
56586             this.el.un("mouseover", this.autoHideHd.mouseover);
56587         }
56588     },
56589
56590     clearMonitor : function(){
56591         Roo.get(document).un("click", this.slideInIf, this);
56592     },
56593
56594     // these names are backwards but not changed for compat
56595     slideOut : function(){
56596         if(this.isSlid || this.el.hasActiveFx()){
56597             return;
56598         }
56599         this.isSlid = true;
56600         if(this.collapseBtn){
56601             this.collapseBtn.hide();
56602         }
56603         this.closeBtnState = this.closeBtn.getStyle('display');
56604         this.closeBtn.hide();
56605         if(this.stickBtn){
56606             this.stickBtn.show();
56607         }
56608         this.el.show();
56609         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56610         this.beforeSlide();
56611         this.el.setStyle("z-index", 10001);
56612         this.el.slideIn(this.getSlideAnchor(), {
56613             callback: function(){
56614                 this.afterSlide();
56615                 this.initAutoHide();
56616                 Roo.get(document).on("click", this.slideInIf, this);
56617                 this.fireEvent("slideshow", this);
56618             },
56619             scope: this,
56620             block: true
56621         });
56622     },
56623
56624     afterSlideIn : function(){
56625         this.clearAutoHide();
56626         this.isSlid = false;
56627         this.clearMonitor();
56628         this.el.setStyle("z-index", "");
56629         if(this.collapseBtn){
56630             this.collapseBtn.show();
56631         }
56632         this.closeBtn.setStyle('display', this.closeBtnState);
56633         if(this.stickBtn){
56634             this.stickBtn.hide();
56635         }
56636         this.fireEvent("slidehide", this);
56637     },
56638
56639     slideIn : function(cb){
56640         if(!this.isSlid || this.el.hasActiveFx()){
56641             Roo.callback(cb);
56642             return;
56643         }
56644         this.isSlid = false;
56645         this.beforeSlide();
56646         this.el.slideOut(this.getSlideAnchor(), {
56647             callback: function(){
56648                 this.el.setLeftTop(-10000, -10000);
56649                 this.afterSlide();
56650                 this.afterSlideIn();
56651                 Roo.callback(cb);
56652             },
56653             scope: this,
56654             block: true
56655         });
56656     },
56657     
56658     slideInIf : function(e){
56659         if(!e.within(this.el)){
56660             this.slideIn();
56661         }
56662     },
56663
56664     animateCollapse : function(){
56665         this.beforeSlide();
56666         this.el.setStyle("z-index", 20000);
56667         var anchor = this.getSlideAnchor();
56668         this.el.slideOut(anchor, {
56669             callback : function(){
56670                 this.el.setStyle("z-index", "");
56671                 this.collapsedEl.slideIn(anchor, {duration:.3});
56672                 this.afterSlide();
56673                 this.el.setLocation(-10000,-10000);
56674                 this.el.hide();
56675                 this.fireEvent("collapsed", this);
56676             },
56677             scope: this,
56678             block: true
56679         });
56680     },
56681
56682     animateExpand : function(){
56683         this.beforeSlide();
56684         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56685         this.el.setStyle("z-index", 20000);
56686         this.collapsedEl.hide({
56687             duration:.1
56688         });
56689         this.el.slideIn(this.getSlideAnchor(), {
56690             callback : function(){
56691                 this.el.setStyle("z-index", "");
56692                 this.afterSlide();
56693                 if(this.split){
56694                     this.split.el.show();
56695                 }
56696                 this.fireEvent("invalidated", this);
56697                 this.fireEvent("expanded", this);
56698             },
56699             scope: this,
56700             block: true
56701         });
56702     },
56703
56704     anchors : {
56705         "west" : "left",
56706         "east" : "right",
56707         "north" : "top",
56708         "south" : "bottom"
56709     },
56710
56711     sanchors : {
56712         "west" : "l",
56713         "east" : "r",
56714         "north" : "t",
56715         "south" : "b"
56716     },
56717
56718     canchors : {
56719         "west" : "tl-tr",
56720         "east" : "tr-tl",
56721         "north" : "tl-bl",
56722         "south" : "bl-tl"
56723     },
56724
56725     getAnchor : function(){
56726         return this.anchors[this.position];
56727     },
56728
56729     getCollapseAnchor : function(){
56730         return this.canchors[this.position];
56731     },
56732
56733     getSlideAnchor : function(){
56734         return this.sanchors[this.position];
56735     },
56736
56737     getAlignAdj : function(){
56738         var cm = this.cmargins;
56739         switch(this.position){
56740             case "west":
56741                 return [0, 0];
56742             break;
56743             case "east":
56744                 return [0, 0];
56745             break;
56746             case "north":
56747                 return [0, 0];
56748             break;
56749             case "south":
56750                 return [0, 0];
56751             break;
56752         }
56753     },
56754
56755     getExpandAdj : function(){
56756         var c = this.collapsedEl, cm = this.cmargins;
56757         switch(this.position){
56758             case "west":
56759                 return [-(cm.right+c.getWidth()+cm.left), 0];
56760             break;
56761             case "east":
56762                 return [cm.right+c.getWidth()+cm.left, 0];
56763             break;
56764             case "north":
56765                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56766             break;
56767             case "south":
56768                 return [0, cm.top+cm.bottom+c.getHeight()];
56769             break;
56770         }
56771     }
56772 });/*
56773  * Based on:
56774  * Ext JS Library 1.1.1
56775  * Copyright(c) 2006-2007, Ext JS, LLC.
56776  *
56777  * Originally Released Under LGPL - original licence link has changed is not relivant.
56778  *
56779  * Fork - LGPL
56780  * <script type="text/javascript">
56781  */
56782 /*
56783  * These classes are private internal classes
56784  */
56785 Roo.CenterLayoutRegion = function(mgr, config){
56786     Roo.LayoutRegion.call(this, mgr, config, "center");
56787     this.visible = true;
56788     this.minWidth = config.minWidth || 20;
56789     this.minHeight = config.minHeight || 20;
56790 };
56791
56792 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56793     hide : function(){
56794         // center panel can't be hidden
56795     },
56796     
56797     show : function(){
56798         // center panel can't be hidden
56799     },
56800     
56801     getMinWidth: function(){
56802         return this.minWidth;
56803     },
56804     
56805     getMinHeight: function(){
56806         return this.minHeight;
56807     }
56808 });
56809
56810
56811 Roo.NorthLayoutRegion = function(mgr, config){
56812     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56813     if(this.split){
56814         this.split.placement = Roo.SplitBar.TOP;
56815         this.split.orientation = Roo.SplitBar.VERTICAL;
56816         this.split.el.addClass("x-layout-split-v");
56817     }
56818     var size = config.initialSize || config.height;
56819     if(typeof size != "undefined"){
56820         this.el.setHeight(size);
56821     }
56822 };
56823 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56824     orientation: Roo.SplitBar.VERTICAL,
56825     getBox : function(){
56826         if(this.collapsed){
56827             return this.collapsedEl.getBox();
56828         }
56829         var box = this.el.getBox();
56830         if(this.split){
56831             box.height += this.split.el.getHeight();
56832         }
56833         return box;
56834     },
56835     
56836     updateBox : function(box){
56837         if(this.split && !this.collapsed){
56838             box.height -= this.split.el.getHeight();
56839             this.split.el.setLeft(box.x);
56840             this.split.el.setTop(box.y+box.height);
56841             this.split.el.setWidth(box.width);
56842         }
56843         if(this.collapsed){
56844             this.updateBody(box.width, null);
56845         }
56846         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56847     }
56848 });
56849
56850 Roo.SouthLayoutRegion = function(mgr, config){
56851     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56852     if(this.split){
56853         this.split.placement = Roo.SplitBar.BOTTOM;
56854         this.split.orientation = Roo.SplitBar.VERTICAL;
56855         this.split.el.addClass("x-layout-split-v");
56856     }
56857     var size = config.initialSize || config.height;
56858     if(typeof size != "undefined"){
56859         this.el.setHeight(size);
56860     }
56861 };
56862 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56863     orientation: Roo.SplitBar.VERTICAL,
56864     getBox : function(){
56865         if(this.collapsed){
56866             return this.collapsedEl.getBox();
56867         }
56868         var box = this.el.getBox();
56869         if(this.split){
56870             var sh = this.split.el.getHeight();
56871             box.height += sh;
56872             box.y -= sh;
56873         }
56874         return box;
56875     },
56876     
56877     updateBox : function(box){
56878         if(this.split && !this.collapsed){
56879             var sh = this.split.el.getHeight();
56880             box.height -= sh;
56881             box.y += sh;
56882             this.split.el.setLeft(box.x);
56883             this.split.el.setTop(box.y-sh);
56884             this.split.el.setWidth(box.width);
56885         }
56886         if(this.collapsed){
56887             this.updateBody(box.width, null);
56888         }
56889         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56890     }
56891 });
56892
56893 Roo.EastLayoutRegion = function(mgr, config){
56894     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56895     if(this.split){
56896         this.split.placement = Roo.SplitBar.RIGHT;
56897         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56898         this.split.el.addClass("x-layout-split-h");
56899     }
56900     var size = config.initialSize || config.width;
56901     if(typeof size != "undefined"){
56902         this.el.setWidth(size);
56903     }
56904 };
56905 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56906     orientation: Roo.SplitBar.HORIZONTAL,
56907     getBox : function(){
56908         if(this.collapsed){
56909             return this.collapsedEl.getBox();
56910         }
56911         var box = this.el.getBox();
56912         if(this.split){
56913             var sw = this.split.el.getWidth();
56914             box.width += sw;
56915             box.x -= sw;
56916         }
56917         return box;
56918     },
56919
56920     updateBox : function(box){
56921         if(this.split && !this.collapsed){
56922             var sw = this.split.el.getWidth();
56923             box.width -= sw;
56924             this.split.el.setLeft(box.x);
56925             this.split.el.setTop(box.y);
56926             this.split.el.setHeight(box.height);
56927             box.x += sw;
56928         }
56929         if(this.collapsed){
56930             this.updateBody(null, box.height);
56931         }
56932         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56933     }
56934 });
56935
56936 Roo.WestLayoutRegion = function(mgr, config){
56937     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56938     if(this.split){
56939         this.split.placement = Roo.SplitBar.LEFT;
56940         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56941         this.split.el.addClass("x-layout-split-h");
56942     }
56943     var size = config.initialSize || config.width;
56944     if(typeof size != "undefined"){
56945         this.el.setWidth(size);
56946     }
56947 };
56948 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56949     orientation: Roo.SplitBar.HORIZONTAL,
56950     getBox : function(){
56951         if(this.collapsed){
56952             return this.collapsedEl.getBox();
56953         }
56954         var box = this.el.getBox();
56955         if(this.split){
56956             box.width += this.split.el.getWidth();
56957         }
56958         return box;
56959     },
56960     
56961     updateBox : function(box){
56962         if(this.split && !this.collapsed){
56963             var sw = this.split.el.getWidth();
56964             box.width -= sw;
56965             this.split.el.setLeft(box.x+box.width);
56966             this.split.el.setTop(box.y);
56967             this.split.el.setHeight(box.height);
56968         }
56969         if(this.collapsed){
56970             this.updateBody(null, box.height);
56971         }
56972         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56973     }
56974 });
56975 /*
56976  * Based on:
56977  * Ext JS Library 1.1.1
56978  * Copyright(c) 2006-2007, Ext JS, LLC.
56979  *
56980  * Originally Released Under LGPL - original licence link has changed is not relivant.
56981  *
56982  * Fork - LGPL
56983  * <script type="text/javascript">
56984  */
56985  
56986  
56987 /*
56988  * Private internal class for reading and applying state
56989  */
56990 Roo.LayoutStateManager = function(layout){
56991      // default empty state
56992      this.state = {
56993         north: {},
56994         south: {},
56995         east: {},
56996         west: {}       
56997     };
56998 };
56999
57000 Roo.LayoutStateManager.prototype = {
57001     init : function(layout, provider){
57002         this.provider = provider;
57003         var state = provider.get(layout.id+"-layout-state");
57004         if(state){
57005             var wasUpdating = layout.isUpdating();
57006             if(!wasUpdating){
57007                 layout.beginUpdate();
57008             }
57009             for(var key in state){
57010                 if(typeof state[key] != "function"){
57011                     var rstate = state[key];
57012                     var r = layout.getRegion(key);
57013                     if(r && rstate){
57014                         if(rstate.size){
57015                             r.resizeTo(rstate.size);
57016                         }
57017                         if(rstate.collapsed == true){
57018                             r.collapse(true);
57019                         }else{
57020                             r.expand(null, true);
57021                         }
57022                     }
57023                 }
57024             }
57025             if(!wasUpdating){
57026                 layout.endUpdate();
57027             }
57028             this.state = state; 
57029         }
57030         this.layout = layout;
57031         layout.on("regionresized", this.onRegionResized, this);
57032         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57033         layout.on("regionexpanded", this.onRegionExpanded, this);
57034     },
57035     
57036     storeState : function(){
57037         this.provider.set(this.layout.id+"-layout-state", this.state);
57038     },
57039     
57040     onRegionResized : function(region, newSize){
57041         this.state[region.getPosition()].size = newSize;
57042         this.storeState();
57043     },
57044     
57045     onRegionCollapsed : function(region){
57046         this.state[region.getPosition()].collapsed = true;
57047         this.storeState();
57048     },
57049     
57050     onRegionExpanded : function(region){
57051         this.state[region.getPosition()].collapsed = false;
57052         this.storeState();
57053     }
57054 };/*
57055  * Based on:
57056  * Ext JS Library 1.1.1
57057  * Copyright(c) 2006-2007, Ext JS, LLC.
57058  *
57059  * Originally Released Under LGPL - original licence link has changed is not relivant.
57060  *
57061  * Fork - LGPL
57062  * <script type="text/javascript">
57063  */
57064 /**
57065  * @class Roo.ContentPanel
57066  * @extends Roo.util.Observable
57067  * @children Roo.form.Form Roo.JsonView Roo.View
57068  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57069  * A basic ContentPanel element.
57070  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57071  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57072  * @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
57073  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57074  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57075  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57076  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57077  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57078  * @cfg {String} title          The title for this panel
57079  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57080  * @cfg {String} url            Calls {@link #setUrl} with this value
57081  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57082  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57083  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57084  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57085  * @cfg {String}    style  Extra style to add to the content panel
57086  * @cfg {Roo.menu.Menu} menu  popup menu
57087
57088  * @constructor
57089  * Create a new ContentPanel.
57090  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57091  * @param {String/Object} config A string to set only the title or a config object
57092  * @param {String} content (optional) Set the HTML content for this panel
57093  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57094  */
57095 Roo.ContentPanel = function(el, config, content){
57096     
57097      
57098     /*
57099     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57100         config = el;
57101         el = Roo.id();
57102     }
57103     if (config && config.parentLayout) { 
57104         el = config.parentLayout.el.createChild(); 
57105     }
57106     */
57107     if(el.autoCreate){ // xtype is available if this is called from factory
57108         config = el;
57109         el = Roo.id();
57110     }
57111     this.el = Roo.get(el);
57112     if(!this.el && config && config.autoCreate){
57113         if(typeof config.autoCreate == "object"){
57114             if(!config.autoCreate.id){
57115                 config.autoCreate.id = config.id||el;
57116             }
57117             this.el = Roo.DomHelper.append(document.body,
57118                         config.autoCreate, true);
57119         }else{
57120             this.el = Roo.DomHelper.append(document.body,
57121                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57122         }
57123     }
57124     
57125     
57126     this.closable = false;
57127     this.loaded = false;
57128     this.active = false;
57129     if(typeof config == "string"){
57130         this.title = config;
57131     }else{
57132         Roo.apply(this, config);
57133     }
57134     
57135     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57136         this.wrapEl = this.el.wrap();
57137         this.toolbar.container = this.el.insertSibling(false, 'before');
57138         this.toolbar = new Roo.Toolbar(this.toolbar);
57139     }
57140     
57141     // xtype created footer. - not sure if will work as we normally have to render first..
57142     if (this.footer && !this.footer.el && this.footer.xtype) {
57143         if (!this.wrapEl) {
57144             this.wrapEl = this.el.wrap();
57145         }
57146     
57147         this.footer.container = this.wrapEl.createChild();
57148          
57149         this.footer = Roo.factory(this.footer, Roo);
57150         
57151     }
57152     
57153     if(this.resizeEl){
57154         this.resizeEl = Roo.get(this.resizeEl, true);
57155     }else{
57156         this.resizeEl = this.el;
57157     }
57158     // handle view.xtype
57159     
57160  
57161     
57162     
57163     this.addEvents({
57164         /**
57165          * @event activate
57166          * Fires when this panel is activated. 
57167          * @param {Roo.ContentPanel} this
57168          */
57169         "activate" : true,
57170         /**
57171          * @event deactivate
57172          * Fires when this panel is activated. 
57173          * @param {Roo.ContentPanel} this
57174          */
57175         "deactivate" : true,
57176
57177         /**
57178          * @event resize
57179          * Fires when this panel is resized if fitToFrame is true.
57180          * @param {Roo.ContentPanel} this
57181          * @param {Number} width The width after any component adjustments
57182          * @param {Number} height The height after any component adjustments
57183          */
57184         "resize" : true,
57185         
57186          /**
57187          * @event render
57188          * Fires when this tab is created
57189          * @param {Roo.ContentPanel} this
57190          */
57191         "render" : true
57192          
57193         
57194     });
57195     
57196
57197     
57198     
57199     if(this.autoScroll){
57200         this.resizeEl.setStyle("overflow", "auto");
57201     } else {
57202         // fix randome scrolling
57203         this.el.on('scroll', function() {
57204             Roo.log('fix random scolling');
57205             this.scrollTo('top',0); 
57206         });
57207     }
57208     content = content || this.content;
57209     if(content){
57210         this.setContent(content);
57211     }
57212     if(config && config.url){
57213         this.setUrl(this.url, this.params, this.loadOnce);
57214     }
57215     
57216     
57217     
57218     Roo.ContentPanel.superclass.constructor.call(this);
57219     
57220     if (this.view && typeof(this.view.xtype) != 'undefined') {
57221         this.view.el = this.el.appendChild(document.createElement("div"));
57222         this.view = Roo.factory(this.view); 
57223         this.view.render  &&  this.view.render(false, '');  
57224     }
57225     
57226     
57227     this.fireEvent('render', this);
57228 };
57229
57230 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57231     tabTip:'',
57232     setRegion : function(region){
57233         this.region = region;
57234         if(region){
57235            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57236         }else{
57237            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57238         } 
57239     },
57240     
57241     /**
57242      * Returns the toolbar for this Panel if one was configured. 
57243      * @return {Roo.Toolbar} 
57244      */
57245     getToolbar : function(){
57246         return this.toolbar;
57247     },
57248     
57249     setActiveState : function(active){
57250         this.active = active;
57251         if(!active){
57252             this.fireEvent("deactivate", this);
57253         }else{
57254             this.fireEvent("activate", this);
57255         }
57256     },
57257     /**
57258      * Updates this panel's element
57259      * @param {String} content The new content
57260      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57261     */
57262     setContent : function(content, loadScripts){
57263         this.el.update(content, loadScripts);
57264     },
57265
57266     ignoreResize : function(w, h){
57267         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57268             return true;
57269         }else{
57270             this.lastSize = {width: w, height: h};
57271             return false;
57272         }
57273     },
57274     /**
57275      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57276      * @return {Roo.UpdateManager} The UpdateManager
57277      */
57278     getUpdateManager : function(){
57279         return this.el.getUpdateManager();
57280     },
57281      /**
57282      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57283      * @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:
57284 <pre><code>
57285 panel.load({
57286     url: "your-url.php",
57287     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57288     callback: yourFunction,
57289     scope: yourObject, //(optional scope)
57290     discardUrl: false,
57291     nocache: false,
57292     text: "Loading...",
57293     timeout: 30,
57294     scripts: false
57295 });
57296 </code></pre>
57297      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57298      * 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.
57299      * @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}
57300      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57301      * @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.
57302      * @return {Roo.ContentPanel} this
57303      */
57304     load : function(){
57305         var um = this.el.getUpdateManager();
57306         um.update.apply(um, arguments);
57307         return this;
57308     },
57309
57310
57311     /**
57312      * 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.
57313      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57314      * @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)
57315      * @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)
57316      * @return {Roo.UpdateManager} The UpdateManager
57317      */
57318     setUrl : function(url, params, loadOnce){
57319         if(this.refreshDelegate){
57320             this.removeListener("activate", this.refreshDelegate);
57321         }
57322         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57323         this.on("activate", this.refreshDelegate);
57324         return this.el.getUpdateManager();
57325     },
57326     
57327     _handleRefresh : function(url, params, loadOnce){
57328         if(!loadOnce || !this.loaded){
57329             var updater = this.el.getUpdateManager();
57330             updater.update(url, params, this._setLoaded.createDelegate(this));
57331         }
57332     },
57333     
57334     _setLoaded : function(){
57335         this.loaded = true;
57336     }, 
57337     
57338     /**
57339      * Returns this panel's id
57340      * @return {String} 
57341      */
57342     getId : function(){
57343         return this.el.id;
57344     },
57345     
57346     /** 
57347      * Returns this panel's element - used by regiosn to add.
57348      * @return {Roo.Element} 
57349      */
57350     getEl : function(){
57351         return this.wrapEl || this.el;
57352     },
57353     
57354     adjustForComponents : function(width, height)
57355     {
57356         //Roo.log('adjustForComponents ');
57357         if(this.resizeEl != this.el){
57358             width -= this.el.getFrameWidth('lr');
57359             height -= this.el.getFrameWidth('tb');
57360         }
57361         if(this.toolbar){
57362             var te = this.toolbar.getEl();
57363             height -= te.getHeight();
57364             te.setWidth(width);
57365         }
57366         if(this.footer){
57367             var te = this.footer.getEl();
57368             //Roo.log("footer:" + te.getHeight());
57369             
57370             height -= te.getHeight();
57371             te.setWidth(width);
57372         }
57373         
57374         
57375         if(this.adjustments){
57376             width += this.adjustments[0];
57377             height += this.adjustments[1];
57378         }
57379         return {"width": width, "height": height};
57380     },
57381     
57382     setSize : function(width, height){
57383         if(this.fitToFrame && !this.ignoreResize(width, height)){
57384             if(this.fitContainer && this.resizeEl != this.el){
57385                 this.el.setSize(width, height);
57386             }
57387             var size = this.adjustForComponents(width, height);
57388             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57389             this.fireEvent('resize', this, size.width, size.height);
57390         }
57391     },
57392     
57393     /**
57394      * Returns this panel's title
57395      * @return {String} 
57396      */
57397     getTitle : function(){
57398         return this.title;
57399     },
57400     
57401     /**
57402      * Set this panel's title
57403      * @param {String} title
57404      */
57405     setTitle : function(title){
57406         this.title = title;
57407         if(this.region){
57408             this.region.updatePanelTitle(this, title);
57409         }
57410     },
57411     
57412     /**
57413      * Returns true is this panel was configured to be closable
57414      * @return {Boolean} 
57415      */
57416     isClosable : function(){
57417         return this.closable;
57418     },
57419     
57420     beforeSlide : function(){
57421         this.el.clip();
57422         this.resizeEl.clip();
57423     },
57424     
57425     afterSlide : function(){
57426         this.el.unclip();
57427         this.resizeEl.unclip();
57428     },
57429     
57430     /**
57431      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57432      *   Will fail silently if the {@link #setUrl} method has not been called.
57433      *   This does not activate the panel, just updates its content.
57434      */
57435     refresh : function(){
57436         if(this.refreshDelegate){
57437            this.loaded = false;
57438            this.refreshDelegate();
57439         }
57440     },
57441     
57442     /**
57443      * Destroys this panel
57444      */
57445     destroy : function(){
57446         this.el.removeAllListeners();
57447         var tempEl = document.createElement("span");
57448         tempEl.appendChild(this.el.dom);
57449         tempEl.innerHTML = "";
57450         this.el.remove();
57451         this.el = null;
57452     },
57453     
57454     /**
57455      * form - if the content panel contains a form - this is a reference to it.
57456      * @type {Roo.form.Form}
57457      */
57458     form : false,
57459     /**
57460      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57461      *    This contains a reference to it.
57462      * @type {Roo.View}
57463      */
57464     view : false,
57465     
57466       /**
57467      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57468      * <pre><code>
57469
57470 layout.addxtype({
57471        xtype : 'Form',
57472        items: [ .... ]
57473    }
57474 );
57475
57476 </code></pre>
57477      * @param {Object} cfg Xtype definition of item to add.
57478      */
57479     
57480     addxtype : function(cfg) {
57481         // add form..
57482         if (cfg.xtype.match(/^Form$/)) {
57483             
57484             var el;
57485             //if (this.footer) {
57486             //    el = this.footer.container.insertSibling(false, 'before');
57487             //} else {
57488                 el = this.el.createChild();
57489             //}
57490
57491             this.form = new  Roo.form.Form(cfg);
57492             
57493             
57494             if ( this.form.allItems.length) {
57495                 this.form.render(el.dom);
57496             }
57497             return this.form;
57498         }
57499         // should only have one of theses..
57500         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57501             // views.. should not be just added - used named prop 'view''
57502             
57503             cfg.el = this.el.appendChild(document.createElement("div"));
57504             // factory?
57505             
57506             var ret = new Roo.factory(cfg);
57507              
57508              ret.render && ret.render(false, ''); // render blank..
57509             this.view = ret;
57510             return ret;
57511         }
57512         return false;
57513     }
57514 });
57515
57516 /**
57517  * @class Roo.GridPanel
57518  * @extends Roo.ContentPanel
57519  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57520  * @constructor
57521  * Create a new GridPanel.
57522  * @cfg {Roo.grid.Grid} grid The grid for this panel
57523  */
57524 Roo.GridPanel = function(grid, config){
57525     
57526     // universal ctor...
57527     if (typeof(grid.grid) != 'undefined') {
57528         config = grid;
57529         grid = config.grid;
57530     }
57531     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57532         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57533         
57534     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57535     
57536     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57537     
57538     if(this.toolbar){
57539         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57540     }
57541     // xtype created footer. - not sure if will work as we normally have to render first..
57542     if (this.footer && !this.footer.el && this.footer.xtype) {
57543         
57544         this.footer.container = this.grid.getView().getFooterPanel(true);
57545         this.footer.dataSource = this.grid.dataSource;
57546         this.footer = Roo.factory(this.footer, Roo);
57547         
57548     }
57549     
57550     grid.monitorWindowResize = false; // turn off autosizing
57551     grid.autoHeight = false;
57552     grid.autoWidth = false;
57553     this.grid = grid;
57554     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57555 };
57556
57557 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57558     getId : function(){
57559         return this.grid.id;
57560     },
57561     
57562     /**
57563      * Returns the grid for this panel
57564      * @return {Roo.grid.Grid} 
57565      */
57566     getGrid : function(){
57567         return this.grid;    
57568     },
57569     
57570     setSize : function(width, height){
57571         if(!this.ignoreResize(width, height)){
57572             var grid = this.grid;
57573             var size = this.adjustForComponents(width, height);
57574             grid.getGridEl().setSize(size.width, size.height);
57575             grid.autoSize();
57576         }
57577     },
57578     
57579     beforeSlide : function(){
57580         this.grid.getView().scroller.clip();
57581     },
57582     
57583     afterSlide : function(){
57584         this.grid.getView().scroller.unclip();
57585     },
57586     
57587     destroy : function(){
57588         this.grid.destroy();
57589         delete this.grid;
57590         Roo.GridPanel.superclass.destroy.call(this); 
57591     }
57592 });
57593
57594
57595 /**
57596  * @class Roo.NestedLayoutPanel
57597  * @extends Roo.ContentPanel
57598  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57599  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57600  *
57601  * 
57602  * @constructor
57603  * Create a new NestedLayoutPanel.
57604  * 
57605  * 
57606  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57607  * @param {String/Object} config A string to set only the title or a config object
57608  */
57609 Roo.NestedLayoutPanel = function(layout, config)
57610 {
57611     // construct with only one argument..
57612     /* FIXME - implement nicer consturctors
57613     if (layout.layout) {
57614         config = layout;
57615         layout = config.layout;
57616         delete config.layout;
57617     }
57618     if (layout.xtype && !layout.getEl) {
57619         // then layout needs constructing..
57620         layout = Roo.factory(layout, Roo);
57621     }
57622     */
57623     
57624     
57625     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57626     
57627     layout.monitorWindowResize = false; // turn off autosizing
57628     this.layout = layout;
57629     this.layout.getEl().addClass("x-layout-nested-layout");
57630     
57631     
57632     
57633     
57634 };
57635
57636 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57637
57638     setSize : function(width, height){
57639         if(!this.ignoreResize(width, height)){
57640             var size = this.adjustForComponents(width, height);
57641             var el = this.layout.getEl();
57642             el.setSize(size.width, size.height);
57643             var touch = el.dom.offsetWidth;
57644             this.layout.layout();
57645             // ie requires a double layout on the first pass
57646             if(Roo.isIE && !this.initialized){
57647                 this.initialized = true;
57648                 this.layout.layout();
57649             }
57650         }
57651     },
57652     
57653     // activate all subpanels if not currently active..
57654     
57655     setActiveState : function(active){
57656         this.active = active;
57657         if(!active){
57658             this.fireEvent("deactivate", this);
57659             return;
57660         }
57661         
57662         this.fireEvent("activate", this);
57663         // not sure if this should happen before or after..
57664         if (!this.layout) {
57665             return; // should not happen..
57666         }
57667         var reg = false;
57668         for (var r in this.layout.regions) {
57669             reg = this.layout.getRegion(r);
57670             if (reg.getActivePanel()) {
57671                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57672                 reg.setActivePanel(reg.getActivePanel());
57673                 continue;
57674             }
57675             if (!reg.panels.length) {
57676                 continue;
57677             }
57678             reg.showPanel(reg.getPanel(0));
57679         }
57680         
57681         
57682         
57683         
57684     },
57685     
57686     /**
57687      * Returns the nested BorderLayout for this panel
57688      * @return {Roo.BorderLayout} 
57689      */
57690     getLayout : function(){
57691         return this.layout;
57692     },
57693     
57694      /**
57695      * Adds a xtype elements to the layout of the nested panel
57696      * <pre><code>
57697
57698 panel.addxtype({
57699        xtype : 'ContentPanel',
57700        region: 'west',
57701        items: [ .... ]
57702    }
57703 );
57704
57705 panel.addxtype({
57706         xtype : 'NestedLayoutPanel',
57707         region: 'west',
57708         layout: {
57709            center: { },
57710            west: { }   
57711         },
57712         items : [ ... list of content panels or nested layout panels.. ]
57713    }
57714 );
57715 </code></pre>
57716      * @param {Object} cfg Xtype definition of item to add.
57717      */
57718     addxtype : function(cfg) {
57719         return this.layout.addxtype(cfg);
57720     
57721     }
57722 });
57723
57724 Roo.ScrollPanel = function(el, config, content){
57725     config = config || {};
57726     config.fitToFrame = true;
57727     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57728     
57729     this.el.dom.style.overflow = "hidden";
57730     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57731     this.el.removeClass("x-layout-inactive-content");
57732     this.el.on("mousewheel", this.onWheel, this);
57733
57734     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57735     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57736     up.unselectable(); down.unselectable();
57737     up.on("click", this.scrollUp, this);
57738     down.on("click", this.scrollDown, this);
57739     up.addClassOnOver("x-scroller-btn-over");
57740     down.addClassOnOver("x-scroller-btn-over");
57741     up.addClassOnClick("x-scroller-btn-click");
57742     down.addClassOnClick("x-scroller-btn-click");
57743     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57744
57745     this.resizeEl = this.el;
57746     this.el = wrap; this.up = up; this.down = down;
57747 };
57748
57749 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57750     increment : 100,
57751     wheelIncrement : 5,
57752     scrollUp : function(){
57753         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57754     },
57755
57756     scrollDown : function(){
57757         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57758     },
57759
57760     afterScroll : function(){
57761         var el = this.resizeEl;
57762         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57763         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57764         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57765     },
57766
57767     setSize : function(){
57768         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57769         this.afterScroll();
57770     },
57771
57772     onWheel : function(e){
57773         var d = e.getWheelDelta();
57774         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57775         this.afterScroll();
57776         e.stopEvent();
57777     },
57778
57779     setContent : function(content, loadScripts){
57780         this.resizeEl.update(content, loadScripts);
57781     }
57782
57783 });
57784
57785
57786
57787 /**
57788  * @class Roo.TreePanel
57789  * @extends Roo.ContentPanel
57790  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57791  * Treepanel component
57792  * 
57793  * @constructor
57794  * Create a new TreePanel. - defaults to fit/scoll contents.
57795  * @param {String/Object} config A string to set only the panel's title, or a config object
57796  */
57797 Roo.TreePanel = function(config){
57798     var el = config.el;
57799     var tree = config.tree;
57800     delete config.tree; 
57801     delete config.el; // hopefull!
57802     
57803     // wrapper for IE7 strict & safari scroll issue
57804     
57805     var treeEl = el.createChild();
57806     config.resizeEl = treeEl;
57807     
57808     
57809     
57810     Roo.TreePanel.superclass.constructor.call(this, el, config);
57811  
57812  
57813     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57814     //console.log(tree);
57815     this.on('activate', function()
57816     {
57817         if (this.tree.rendered) {
57818             return;
57819         }
57820         //console.log('render tree');
57821         this.tree.render();
57822     });
57823     // this should not be needed.. - it's actually the 'el' that resizes?
57824     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57825     
57826     //this.on('resize',  function (cp, w, h) {
57827     //        this.tree.innerCt.setWidth(w);
57828     //        this.tree.innerCt.setHeight(h);
57829     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57830     //});
57831
57832         
57833     
57834 };
57835
57836 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57837     fitToFrame : true,
57838     autoScroll : true,
57839     /*
57840      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57841      */
57842     tree : false
57843
57844 });
57845
57846
57847
57848
57849
57850
57851
57852
57853
57854
57855
57856 /*
57857  * Based on:
57858  * Ext JS Library 1.1.1
57859  * Copyright(c) 2006-2007, Ext JS, LLC.
57860  *
57861  * Originally Released Under LGPL - original licence link has changed is not relivant.
57862  *
57863  * Fork - LGPL
57864  * <script type="text/javascript">
57865  */
57866  
57867
57868 /**
57869  * @class Roo.ReaderLayout
57870  * @extends Roo.BorderLayout
57871  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57872  * center region containing two nested regions (a top one for a list view and one for item preview below),
57873  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57874  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57875  * expedites the setup of the overall layout and regions for this common application style.
57876  * Example:
57877  <pre><code>
57878 var reader = new Roo.ReaderLayout();
57879 var CP = Roo.ContentPanel;  // shortcut for adding
57880
57881 reader.beginUpdate();
57882 reader.add("north", new CP("north", "North"));
57883 reader.add("west", new CP("west", {title: "West"}));
57884 reader.add("east", new CP("east", {title: "East"}));
57885
57886 reader.regions.listView.add(new CP("listView", "List"));
57887 reader.regions.preview.add(new CP("preview", "Preview"));
57888 reader.endUpdate();
57889 </code></pre>
57890 * @constructor
57891 * Create a new ReaderLayout
57892 * @param {Object} config Configuration options
57893 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57894 * document.body if omitted)
57895 */
57896 Roo.ReaderLayout = function(config, renderTo){
57897     var c = config || {size:{}};
57898     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57899         north: c.north !== false ? Roo.apply({
57900             split:false,
57901             initialSize: 32,
57902             titlebar: false
57903         }, c.north) : false,
57904         west: c.west !== false ? Roo.apply({
57905             split:true,
57906             initialSize: 200,
57907             minSize: 175,
57908             maxSize: 400,
57909             titlebar: true,
57910             collapsible: true,
57911             animate: true,
57912             margins:{left:5,right:0,bottom:5,top:5},
57913             cmargins:{left:5,right:5,bottom:5,top:5}
57914         }, c.west) : false,
57915         east: c.east !== false ? Roo.apply({
57916             split:true,
57917             initialSize: 200,
57918             minSize: 175,
57919             maxSize: 400,
57920             titlebar: true,
57921             collapsible: true,
57922             animate: true,
57923             margins:{left:0,right:5,bottom:5,top:5},
57924             cmargins:{left:5,right:5,bottom:5,top:5}
57925         }, c.east) : false,
57926         center: Roo.apply({
57927             tabPosition: 'top',
57928             autoScroll:false,
57929             closeOnTab: true,
57930             titlebar:false,
57931             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57932         }, c.center)
57933     });
57934
57935     this.el.addClass('x-reader');
57936
57937     this.beginUpdate();
57938
57939     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57940         south: c.preview !== false ? Roo.apply({
57941             split:true,
57942             initialSize: 200,
57943             minSize: 100,
57944             autoScroll:true,
57945             collapsible:true,
57946             titlebar: true,
57947             cmargins:{top:5,left:0, right:0, bottom:0}
57948         }, c.preview) : false,
57949         center: Roo.apply({
57950             autoScroll:false,
57951             titlebar:false,
57952             minHeight:200
57953         }, c.listView)
57954     });
57955     this.add('center', new Roo.NestedLayoutPanel(inner,
57956             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57957
57958     this.endUpdate();
57959
57960     this.regions.preview = inner.getRegion('south');
57961     this.regions.listView = inner.getRegion('center');
57962 };
57963
57964 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57965  * Based on:
57966  * Ext JS Library 1.1.1
57967  * Copyright(c) 2006-2007, Ext JS, LLC.
57968  *
57969  * Originally Released Under LGPL - original licence link has changed is not relivant.
57970  *
57971  * Fork - LGPL
57972  * <script type="text/javascript">
57973  */
57974  
57975 /**
57976  * @class Roo.grid.Grid
57977  * @extends Roo.util.Observable
57978  * This class represents the primary interface of a component based grid control.
57979  * <br><br>Usage:<pre><code>
57980  var grid = new Roo.grid.Grid("my-container-id", {
57981      ds: myDataStore,
57982      cm: myColModel,
57983      selModel: mySelectionModel,
57984      autoSizeColumns: true,
57985      monitorWindowResize: false,
57986      trackMouseOver: true
57987  });
57988  // set any options
57989  grid.render();
57990  * </code></pre>
57991  * <b>Common Problems:</b><br/>
57992  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57993  * element will correct this<br/>
57994  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57995  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57996  * are unpredictable.<br/>
57997  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57998  * grid to calculate dimensions/offsets.<br/>
57999   * @constructor
58000  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
58001  * The container MUST have some type of size defined for the grid to fill. The container will be
58002  * automatically set to position relative if it isn't already.
58003  * @param {Object} config A config object that sets properties on this grid.
58004  */
58005 Roo.grid.Grid = function(container, config){
58006         // initialize the container
58007         this.container = Roo.get(container);
58008         this.container.update("");
58009         this.container.setStyle("overflow", "hidden");
58010     this.container.addClass('x-grid-container');
58011
58012     this.id = this.container.id;
58013
58014     Roo.apply(this, config);
58015     // check and correct shorthanded configs
58016     if(this.ds){
58017         this.dataSource = this.ds;
58018         delete this.ds;
58019     }
58020     if(this.cm){
58021         this.colModel = this.cm;
58022         delete this.cm;
58023     }
58024     if(this.sm){
58025         this.selModel = this.sm;
58026         delete this.sm;
58027     }
58028
58029     if (this.selModel) {
58030         this.selModel = Roo.factory(this.selModel, Roo.grid);
58031         this.sm = this.selModel;
58032         this.sm.xmodule = this.xmodule || false;
58033     }
58034     if (typeof(this.colModel.config) == 'undefined') {
58035         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58036         this.cm = this.colModel;
58037         this.cm.xmodule = this.xmodule || false;
58038     }
58039     if (this.dataSource) {
58040         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58041         this.ds = this.dataSource;
58042         this.ds.xmodule = this.xmodule || false;
58043          
58044     }
58045     
58046     
58047     
58048     if(this.width){
58049         this.container.setWidth(this.width);
58050     }
58051
58052     if(this.height){
58053         this.container.setHeight(this.height);
58054     }
58055     /** @private */
58056         this.addEvents({
58057         // raw events
58058         /**
58059          * @event click
58060          * The raw click event for the entire grid.
58061          * @param {Roo.EventObject} e
58062          */
58063         "click" : true,
58064         /**
58065          * @event dblclick
58066          * The raw dblclick event for the entire grid.
58067          * @param {Roo.EventObject} e
58068          */
58069         "dblclick" : true,
58070         /**
58071          * @event contextmenu
58072          * The raw contextmenu event for the entire grid.
58073          * @param {Roo.EventObject} e
58074          */
58075         "contextmenu" : true,
58076         /**
58077          * @event mousedown
58078          * The raw mousedown event for the entire grid.
58079          * @param {Roo.EventObject} e
58080          */
58081         "mousedown" : true,
58082         /**
58083          * @event mouseup
58084          * The raw mouseup event for the entire grid.
58085          * @param {Roo.EventObject} e
58086          */
58087         "mouseup" : true,
58088         /**
58089          * @event mouseover
58090          * The raw mouseover event for the entire grid.
58091          * @param {Roo.EventObject} e
58092          */
58093         "mouseover" : true,
58094         /**
58095          * @event mouseout
58096          * The raw mouseout event for the entire grid.
58097          * @param {Roo.EventObject} e
58098          */
58099         "mouseout" : true,
58100         /**
58101          * @event keypress
58102          * The raw keypress event for the entire grid.
58103          * @param {Roo.EventObject} e
58104          */
58105         "keypress" : true,
58106         /**
58107          * @event keydown
58108          * The raw keydown event for the entire grid.
58109          * @param {Roo.EventObject} e
58110          */
58111         "keydown" : true,
58112
58113         // custom events
58114
58115         /**
58116          * @event cellclick
58117          * Fires when a cell is clicked
58118          * @param {Grid} this
58119          * @param {Number} rowIndex
58120          * @param {Number} columnIndex
58121          * @param {Roo.EventObject} e
58122          */
58123         "cellclick" : true,
58124         /**
58125          * @event celldblclick
58126          * Fires when a cell is double clicked
58127          * @param {Grid} this
58128          * @param {Number} rowIndex
58129          * @param {Number} columnIndex
58130          * @param {Roo.EventObject} e
58131          */
58132         "celldblclick" : true,
58133         /**
58134          * @event rowclick
58135          * Fires when a row is clicked
58136          * @param {Grid} this
58137          * @param {Number} rowIndex
58138          * @param {Roo.EventObject} e
58139          */
58140         "rowclick" : true,
58141         /**
58142          * @event rowdblclick
58143          * Fires when a row is double clicked
58144          * @param {Grid} this
58145          * @param {Number} rowIndex
58146          * @param {Roo.EventObject} e
58147          */
58148         "rowdblclick" : true,
58149         /**
58150          * @event headerclick
58151          * Fires when a header is clicked
58152          * @param {Grid} this
58153          * @param {Number} columnIndex
58154          * @param {Roo.EventObject} e
58155          */
58156         "headerclick" : true,
58157         /**
58158          * @event headerdblclick
58159          * Fires when a header cell is double clicked
58160          * @param {Grid} this
58161          * @param {Number} columnIndex
58162          * @param {Roo.EventObject} e
58163          */
58164         "headerdblclick" : true,
58165         /**
58166          * @event rowcontextmenu
58167          * Fires when a row is right clicked
58168          * @param {Grid} this
58169          * @param {Number} rowIndex
58170          * @param {Roo.EventObject} e
58171          */
58172         "rowcontextmenu" : true,
58173         /**
58174          * @event cellcontextmenu
58175          * Fires when a cell is right clicked
58176          * @param {Grid} this
58177          * @param {Number} rowIndex
58178          * @param {Number} cellIndex
58179          * @param {Roo.EventObject} e
58180          */
58181          "cellcontextmenu" : true,
58182         /**
58183          * @event headercontextmenu
58184          * Fires when a header is right clicked
58185          * @param {Grid} this
58186          * @param {Number} columnIndex
58187          * @param {Roo.EventObject} e
58188          */
58189         "headercontextmenu" : true,
58190         /**
58191          * @event bodyscroll
58192          * Fires when the body element is scrolled
58193          * @param {Number} scrollLeft
58194          * @param {Number} scrollTop
58195          */
58196         "bodyscroll" : true,
58197         /**
58198          * @event columnresize
58199          * Fires when the user resizes a column
58200          * @param {Number} columnIndex
58201          * @param {Number} newSize
58202          */
58203         "columnresize" : true,
58204         /**
58205          * @event columnmove
58206          * Fires when the user moves a column
58207          * @param {Number} oldIndex
58208          * @param {Number} newIndex
58209          */
58210         "columnmove" : true,
58211         /**
58212          * @event startdrag
58213          * Fires when row(s) start being dragged
58214          * @param {Grid} this
58215          * @param {Roo.GridDD} dd The drag drop object
58216          * @param {event} e The raw browser event
58217          */
58218         "startdrag" : true,
58219         /**
58220          * @event enddrag
58221          * Fires when a drag operation is complete
58222          * @param {Grid} this
58223          * @param {Roo.GridDD} dd The drag drop object
58224          * @param {event} e The raw browser event
58225          */
58226         "enddrag" : true,
58227         /**
58228          * @event dragdrop
58229          * Fires when dragged row(s) are dropped on a valid DD target
58230          * @param {Grid} this
58231          * @param {Roo.GridDD} dd The drag drop object
58232          * @param {String} targetId The target drag drop object
58233          * @param {event} e The raw browser event
58234          */
58235         "dragdrop" : true,
58236         /**
58237          * @event dragover
58238          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58239          * @param {Grid} this
58240          * @param {Roo.GridDD} dd The drag drop object
58241          * @param {String} targetId The target drag drop object
58242          * @param {event} e The raw browser event
58243          */
58244         "dragover" : true,
58245         /**
58246          * @event dragenter
58247          *  Fires when the dragged row(s) first cross another DD target while being dragged
58248          * @param {Grid} this
58249          * @param {Roo.GridDD} dd The drag drop object
58250          * @param {String} targetId The target drag drop object
58251          * @param {event} e The raw browser event
58252          */
58253         "dragenter" : true,
58254         /**
58255          * @event dragout
58256          * Fires when the dragged row(s) leave another DD target while being dragged
58257          * @param {Grid} this
58258          * @param {Roo.GridDD} dd The drag drop object
58259          * @param {String} targetId The target drag drop object
58260          * @param {event} e The raw browser event
58261          */
58262         "dragout" : true,
58263         /**
58264          * @event rowclass
58265          * Fires when a row is rendered, so you can change add a style to it.
58266          * @param {GridView} gridview   The grid view
58267          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58268          */
58269         'rowclass' : true,
58270
58271         /**
58272          * @event render
58273          * Fires when the grid is rendered
58274          * @param {Grid} grid
58275          */
58276         'render' : true
58277     });
58278
58279     Roo.grid.Grid.superclass.constructor.call(this);
58280 };
58281 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58282     
58283     /**
58284          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58285          */
58286         /**
58287          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58288          */
58289         /**
58290          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58291          */
58292         /**
58293          * @cfg {Roo.grid.Store} ds The data store for the grid
58294          */
58295         /**
58296          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58297          */
58298         /**
58299      * @cfg {String} ddGroup - drag drop group.
58300      */
58301       /**
58302      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58303      */
58304
58305     /**
58306      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58307      */
58308     minColumnWidth : 25,
58309
58310     /**
58311      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58312      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58313      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58314      */
58315     autoSizeColumns : false,
58316
58317     /**
58318      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58319      */
58320     autoSizeHeaders : true,
58321
58322     /**
58323      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58324      */
58325     monitorWindowResize : true,
58326
58327     /**
58328      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58329      * rows measured to get a columns size. Default is 0 (all rows).
58330      */
58331     maxRowsToMeasure : 0,
58332
58333     /**
58334      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58335      */
58336     trackMouseOver : true,
58337
58338     /**
58339     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58340     */
58341       /**
58342     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58343     */
58344     
58345     /**
58346     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58347     */
58348     enableDragDrop : false,
58349     
58350     /**
58351     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58352     */
58353     enableColumnMove : true,
58354     
58355     /**
58356     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58357     */
58358     enableColumnHide : true,
58359     
58360     /**
58361     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58362     */
58363     enableRowHeightSync : false,
58364     
58365     /**
58366     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58367     */
58368     stripeRows : true,
58369     
58370     /**
58371     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58372     */
58373     autoHeight : false,
58374
58375     /**
58376      * @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.
58377      */
58378     autoExpandColumn : false,
58379
58380     /**
58381     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58382     * Default is 50.
58383     */
58384     autoExpandMin : 50,
58385
58386     /**
58387     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58388     */
58389     autoExpandMax : 1000,
58390
58391     /**
58392     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58393     */
58394     view : null,
58395
58396     /**
58397     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58398     */
58399     loadMask : false,
58400     /**
58401     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58402     */
58403     dropTarget: false,
58404      /**
58405     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58406     */ 
58407     sortColMenu : false,
58408     
58409     // private
58410     rendered : false,
58411
58412     /**
58413     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58414     * of a fixed width. Default is false.
58415     */
58416     /**
58417     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58418     */
58419     
58420     
58421     /**
58422     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58423     * %0 is replaced with the number of selected rows.
58424     */
58425     ddText : "{0} selected row{1}",
58426     
58427     
58428     /**
58429      * Called once after all setup has been completed and the grid is ready to be rendered.
58430      * @return {Roo.grid.Grid} this
58431      */
58432     render : function()
58433     {
58434         var c = this.container;
58435         // try to detect autoHeight/width mode
58436         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58437             this.autoHeight = true;
58438         }
58439         var view = this.getView();
58440         view.init(this);
58441
58442         c.on("click", this.onClick, this);
58443         c.on("dblclick", this.onDblClick, this);
58444         c.on("contextmenu", this.onContextMenu, this);
58445         c.on("keydown", this.onKeyDown, this);
58446         if (Roo.isTouch) {
58447             c.on("touchstart", this.onTouchStart, this);
58448         }
58449
58450         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58451
58452         this.getSelectionModel().init(this);
58453
58454         view.render();
58455
58456         if(this.loadMask){
58457             this.loadMask = new Roo.LoadMask(this.container,
58458                     Roo.apply({store:this.dataSource}, this.loadMask));
58459         }
58460         
58461         
58462         if (this.toolbar && this.toolbar.xtype) {
58463             this.toolbar.container = this.getView().getHeaderPanel(true);
58464             this.toolbar = new Roo.Toolbar(this.toolbar);
58465         }
58466         if (this.footer && this.footer.xtype) {
58467             this.footer.dataSource = this.getDataSource();
58468             this.footer.container = this.getView().getFooterPanel(true);
58469             this.footer = Roo.factory(this.footer, Roo);
58470         }
58471         if (this.dropTarget && this.dropTarget.xtype) {
58472             delete this.dropTarget.xtype;
58473             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58474         }
58475         
58476         
58477         this.rendered = true;
58478         this.fireEvent('render', this);
58479         return this;
58480     },
58481
58482     /**
58483      * Reconfigures the grid to use a different Store and Column Model.
58484      * The View will be bound to the new objects and refreshed.
58485      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58486      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58487      */
58488     reconfigure : function(dataSource, colModel){
58489         if(this.loadMask){
58490             this.loadMask.destroy();
58491             this.loadMask = new Roo.LoadMask(this.container,
58492                     Roo.apply({store:dataSource}, this.loadMask));
58493         }
58494         this.view.bind(dataSource, colModel);
58495         this.dataSource = dataSource;
58496         this.colModel = colModel;
58497         this.view.refresh(true);
58498     },
58499     /**
58500      * addColumns
58501      * Add's a column, default at the end..
58502      
58503      * @param {int} position to add (default end)
58504      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58505      */
58506     addColumns : function(pos, ar)
58507     {
58508         
58509         for (var i =0;i< ar.length;i++) {
58510             var cfg = ar[i];
58511             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58512             this.cm.lookup[cfg.id] = cfg;
58513         }
58514         
58515         
58516         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58517             pos = this.cm.config.length; //this.cm.config.push(cfg);
58518         } 
58519         pos = Math.max(0,pos);
58520         ar.unshift(0);
58521         ar.unshift(pos);
58522         this.cm.config.splice.apply(this.cm.config, ar);
58523         
58524         
58525         
58526         this.view.generateRules(this.cm);
58527         this.view.refresh(true);
58528         
58529     },
58530     
58531     
58532     
58533     
58534     // private
58535     onKeyDown : function(e){
58536         this.fireEvent("keydown", e);
58537     },
58538
58539     /**
58540      * Destroy this grid.
58541      * @param {Boolean} removeEl True to remove the element
58542      */
58543     destroy : function(removeEl, keepListeners){
58544         if(this.loadMask){
58545             this.loadMask.destroy();
58546         }
58547         var c = this.container;
58548         c.removeAllListeners();
58549         this.view.destroy();
58550         this.colModel.purgeListeners();
58551         if(!keepListeners){
58552             this.purgeListeners();
58553         }
58554         c.update("");
58555         if(removeEl === true){
58556             c.remove();
58557         }
58558     },
58559
58560     // private
58561     processEvent : function(name, e){
58562         // does this fire select???
58563         //Roo.log('grid:processEvent '  + name);
58564         
58565         if (name != 'touchstart' ) {
58566             this.fireEvent(name, e);    
58567         }
58568         
58569         var t = e.getTarget();
58570         var v = this.view;
58571         var header = v.findHeaderIndex(t);
58572         if(header !== false){
58573             var ename = name == 'touchstart' ? 'click' : name;
58574              
58575             this.fireEvent("header" + ename, this, header, e);
58576         }else{
58577             var row = v.findRowIndex(t);
58578             var cell = v.findCellIndex(t);
58579             if (name == 'touchstart') {
58580                 // first touch is always a click.
58581                 // hopefull this happens after selection is updated.?
58582                 name = false;
58583                 
58584                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58585                     var cs = this.selModel.getSelectedCell();
58586                     if (row == cs[0] && cell == cs[1]){
58587                         name = 'dblclick';
58588                     }
58589                 }
58590                 if (typeof(this.selModel.getSelections) != 'undefined') {
58591                     var cs = this.selModel.getSelections();
58592                     var ds = this.dataSource;
58593                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58594                         name = 'dblclick';
58595                     }
58596                 }
58597                 if (!name) {
58598                     return;
58599                 }
58600             }
58601             
58602             
58603             if(row !== false){
58604                 this.fireEvent("row" + name, this, row, e);
58605                 if(cell !== false){
58606                     this.fireEvent("cell" + name, this, row, cell, e);
58607                 }
58608             }
58609         }
58610     },
58611
58612     // private
58613     onClick : function(e){
58614         this.processEvent("click", e);
58615     },
58616    // private
58617     onTouchStart : function(e){
58618         this.processEvent("touchstart", e);
58619     },
58620
58621     // private
58622     onContextMenu : function(e, t){
58623         this.processEvent("contextmenu", e);
58624     },
58625
58626     // private
58627     onDblClick : function(e){
58628         this.processEvent("dblclick", e);
58629     },
58630
58631     // private
58632     walkCells : function(row, col, step, fn, scope){
58633         var cm = this.colModel, clen = cm.getColumnCount();
58634         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58635         if(step < 0){
58636             if(col < 0){
58637                 row--;
58638                 first = false;
58639             }
58640             while(row >= 0){
58641                 if(!first){
58642                     col = clen-1;
58643                 }
58644                 first = false;
58645                 while(col >= 0){
58646                     if(fn.call(scope || this, row, col, cm) === true){
58647                         return [row, col];
58648                     }
58649                     col--;
58650                 }
58651                 row--;
58652             }
58653         } else {
58654             if(col >= clen){
58655                 row++;
58656                 first = false;
58657             }
58658             while(row < rlen){
58659                 if(!first){
58660                     col = 0;
58661                 }
58662                 first = false;
58663                 while(col < clen){
58664                     if(fn.call(scope || this, row, col, cm) === true){
58665                         return [row, col];
58666                     }
58667                     col++;
58668                 }
58669                 row++;
58670             }
58671         }
58672         return null;
58673     },
58674
58675     // private
58676     getSelections : function(){
58677         return this.selModel.getSelections();
58678     },
58679
58680     /**
58681      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58682      * but if manual update is required this method will initiate it.
58683      */
58684     autoSize : function(){
58685         if(this.rendered){
58686             this.view.layout();
58687             if(this.view.adjustForScroll){
58688                 this.view.adjustForScroll();
58689             }
58690         }
58691     },
58692
58693     /**
58694      * Returns the grid's underlying element.
58695      * @return {Element} The element
58696      */
58697     getGridEl : function(){
58698         return this.container;
58699     },
58700
58701     // private for compatibility, overridden by editor grid
58702     stopEditing : function(){},
58703
58704     /**
58705      * Returns the grid's SelectionModel.
58706      * @return {SelectionModel}
58707      */
58708     getSelectionModel : function(){
58709         if(!this.selModel){
58710             this.selModel = new Roo.grid.RowSelectionModel();
58711         }
58712         return this.selModel;
58713     },
58714
58715     /**
58716      * Returns the grid's DataSource.
58717      * @return {DataSource}
58718      */
58719     getDataSource : function(){
58720         return this.dataSource;
58721     },
58722
58723     /**
58724      * Returns the grid's ColumnModel.
58725      * @return {ColumnModel}
58726      */
58727     getColumnModel : function(){
58728         return this.colModel;
58729     },
58730
58731     /**
58732      * Returns the grid's GridView object.
58733      * @return {GridView}
58734      */
58735     getView : function(){
58736         if(!this.view){
58737             this.view = new Roo.grid.GridView(this.viewConfig);
58738             this.relayEvents(this.view, [
58739                 "beforerowremoved", "beforerowsinserted",
58740                 "beforerefresh", "rowremoved",
58741                 "rowsinserted", "rowupdated" ,"refresh"
58742             ]);
58743         }
58744         return this.view;
58745     },
58746     /**
58747      * Called to get grid's drag proxy text, by default returns this.ddText.
58748      * Override this to put something different in the dragged text.
58749      * @return {String}
58750      */
58751     getDragDropText : function(){
58752         var count = this.selModel.getCount();
58753         return String.format(this.ddText, count, count == 1 ? '' : 's');
58754     }
58755 });
58756 /*
58757  * Based on:
58758  * Ext JS Library 1.1.1
58759  * Copyright(c) 2006-2007, Ext JS, LLC.
58760  *
58761  * Originally Released Under LGPL - original licence link has changed is not relivant.
58762  *
58763  * Fork - LGPL
58764  * <script type="text/javascript">
58765  */
58766  /**
58767  * @class Roo.grid.AbstractGridView
58768  * @extends Roo.util.Observable
58769  * @abstract
58770  * Abstract base class for grid Views
58771  * @constructor
58772  */
58773 Roo.grid.AbstractGridView = function(){
58774         this.grid = null;
58775         
58776         this.events = {
58777             "beforerowremoved" : true,
58778             "beforerowsinserted" : true,
58779             "beforerefresh" : true,
58780             "rowremoved" : true,
58781             "rowsinserted" : true,
58782             "rowupdated" : true,
58783             "refresh" : true
58784         };
58785     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58786 };
58787
58788 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58789     rowClass : "x-grid-row",
58790     cellClass : "x-grid-cell",
58791     tdClass : "x-grid-td",
58792     hdClass : "x-grid-hd",
58793     splitClass : "x-grid-hd-split",
58794     
58795     init: function(grid){
58796         this.grid = grid;
58797                 var cid = this.grid.getGridEl().id;
58798         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58799         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58800         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58801         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58802         },
58803         
58804     getColumnRenderers : function(){
58805         var renderers = [];
58806         var cm = this.grid.colModel;
58807         var colCount = cm.getColumnCount();
58808         for(var i = 0; i < colCount; i++){
58809             renderers[i] = cm.getRenderer(i);
58810         }
58811         return renderers;
58812     },
58813     
58814     getColumnIds : function(){
58815         var ids = [];
58816         var cm = this.grid.colModel;
58817         var colCount = cm.getColumnCount();
58818         for(var i = 0; i < colCount; i++){
58819             ids[i] = cm.getColumnId(i);
58820         }
58821         return ids;
58822     },
58823     
58824     getDataIndexes : function(){
58825         if(!this.indexMap){
58826             this.indexMap = this.buildIndexMap();
58827         }
58828         return this.indexMap.colToData;
58829     },
58830     
58831     getColumnIndexByDataIndex : function(dataIndex){
58832         if(!this.indexMap){
58833             this.indexMap = this.buildIndexMap();
58834         }
58835         return this.indexMap.dataToCol[dataIndex];
58836     },
58837     
58838     /**
58839      * Set a css style for a column dynamically. 
58840      * @param {Number} colIndex The index of the column
58841      * @param {String} name The css property name
58842      * @param {String} value The css value
58843      */
58844     setCSSStyle : function(colIndex, name, value){
58845         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58846         Roo.util.CSS.updateRule(selector, name, value);
58847     },
58848     
58849     generateRules : function(cm){
58850         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58851         Roo.util.CSS.removeStyleSheet(rulesId);
58852         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58853             var cid = cm.getColumnId(i);
58854             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58855                          this.tdSelector, cid, " {\n}\n",
58856                          this.hdSelector, cid, " {\n}\n",
58857                          this.splitSelector, cid, " {\n}\n");
58858         }
58859         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58860     }
58861 });/*
58862  * Based on:
58863  * Ext JS Library 1.1.1
58864  * Copyright(c) 2006-2007, Ext JS, LLC.
58865  *
58866  * Originally Released Under LGPL - original licence link has changed is not relivant.
58867  *
58868  * Fork - LGPL
58869  * <script type="text/javascript">
58870  */
58871
58872 // private
58873 // This is a support class used internally by the Grid components
58874 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58875     this.grid = grid;
58876     this.view = grid.getView();
58877     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58878     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58879     if(hd2){
58880         this.setHandleElId(Roo.id(hd));
58881         this.setOuterHandleElId(Roo.id(hd2));
58882     }
58883     this.scroll = false;
58884 };
58885 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58886     maxDragWidth: 120,
58887     getDragData : function(e){
58888         var t = Roo.lib.Event.getTarget(e);
58889         var h = this.view.findHeaderCell(t);
58890         if(h){
58891             return {ddel: h.firstChild, header:h};
58892         }
58893         return false;
58894     },
58895
58896     onInitDrag : function(e){
58897         this.view.headersDisabled = true;
58898         var clone = this.dragData.ddel.cloneNode(true);
58899         clone.id = Roo.id();
58900         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58901         this.proxy.update(clone);
58902         return true;
58903     },
58904
58905     afterValidDrop : function(){
58906         var v = this.view;
58907         setTimeout(function(){
58908             v.headersDisabled = false;
58909         }, 50);
58910     },
58911
58912     afterInvalidDrop : function(){
58913         var v = this.view;
58914         setTimeout(function(){
58915             v.headersDisabled = false;
58916         }, 50);
58917     }
58918 });
58919 /*
58920  * Based on:
58921  * Ext JS Library 1.1.1
58922  * Copyright(c) 2006-2007, Ext JS, LLC.
58923  *
58924  * Originally Released Under LGPL - original licence link has changed is not relivant.
58925  *
58926  * Fork - LGPL
58927  * <script type="text/javascript">
58928  */
58929 // private
58930 // This is a support class used internally by the Grid components
58931 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58932     this.grid = grid;
58933     this.view = grid.getView();
58934     // split the proxies so they don't interfere with mouse events
58935     this.proxyTop = Roo.DomHelper.append(document.body, {
58936         cls:"col-move-top", html:"&#160;"
58937     }, true);
58938     this.proxyBottom = Roo.DomHelper.append(document.body, {
58939         cls:"col-move-bottom", html:"&#160;"
58940     }, true);
58941     this.proxyTop.hide = this.proxyBottom.hide = function(){
58942         this.setLeftTop(-100,-100);
58943         this.setStyle("visibility", "hidden");
58944     };
58945     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58946     // temporarily disabled
58947     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58948     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58949 };
58950 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58951     proxyOffsets : [-4, -9],
58952     fly: Roo.Element.fly,
58953
58954     getTargetFromEvent : function(e){
58955         var t = Roo.lib.Event.getTarget(e);
58956         var cindex = this.view.findCellIndex(t);
58957         if(cindex !== false){
58958             return this.view.getHeaderCell(cindex);
58959         }
58960         return null;
58961     },
58962
58963     nextVisible : function(h){
58964         var v = this.view, cm = this.grid.colModel;
58965         h = h.nextSibling;
58966         while(h){
58967             if(!cm.isHidden(v.getCellIndex(h))){
58968                 return h;
58969             }
58970             h = h.nextSibling;
58971         }
58972         return null;
58973     },
58974
58975     prevVisible : function(h){
58976         var v = this.view, cm = this.grid.colModel;
58977         h = h.prevSibling;
58978         while(h){
58979             if(!cm.isHidden(v.getCellIndex(h))){
58980                 return h;
58981             }
58982             h = h.prevSibling;
58983         }
58984         return null;
58985     },
58986
58987     positionIndicator : function(h, n, e){
58988         var x = Roo.lib.Event.getPageX(e);
58989         var r = Roo.lib.Dom.getRegion(n.firstChild);
58990         var px, pt, py = r.top + this.proxyOffsets[1];
58991         if((r.right - x) <= (r.right-r.left)/2){
58992             px = r.right+this.view.borderWidth;
58993             pt = "after";
58994         }else{
58995             px = r.left;
58996             pt = "before";
58997         }
58998         var oldIndex = this.view.getCellIndex(h);
58999         var newIndex = this.view.getCellIndex(n);
59000
59001         if(this.grid.colModel.isFixed(newIndex)){
59002             return false;
59003         }
59004
59005         var locked = this.grid.colModel.isLocked(newIndex);
59006
59007         if(pt == "after"){
59008             newIndex++;
59009         }
59010         if(oldIndex < newIndex){
59011             newIndex--;
59012         }
59013         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59014             return false;
59015         }
59016         px +=  this.proxyOffsets[0];
59017         this.proxyTop.setLeftTop(px, py);
59018         this.proxyTop.show();
59019         if(!this.bottomOffset){
59020             this.bottomOffset = this.view.mainHd.getHeight();
59021         }
59022         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59023         this.proxyBottom.show();
59024         return pt;
59025     },
59026
59027     onNodeEnter : function(n, dd, e, data){
59028         if(data.header != n){
59029             this.positionIndicator(data.header, n, e);
59030         }
59031     },
59032
59033     onNodeOver : function(n, dd, e, data){
59034         var result = false;
59035         if(data.header != n){
59036             result = this.positionIndicator(data.header, n, e);
59037         }
59038         if(!result){
59039             this.proxyTop.hide();
59040             this.proxyBottom.hide();
59041         }
59042         return result ? this.dropAllowed : this.dropNotAllowed;
59043     },
59044
59045     onNodeOut : function(n, dd, e, data){
59046         this.proxyTop.hide();
59047         this.proxyBottom.hide();
59048     },
59049
59050     onNodeDrop : function(n, dd, e, data){
59051         var h = data.header;
59052         if(h != n){
59053             var cm = this.grid.colModel;
59054             var x = Roo.lib.Event.getPageX(e);
59055             var r = Roo.lib.Dom.getRegion(n.firstChild);
59056             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59057             var oldIndex = this.view.getCellIndex(h);
59058             var newIndex = this.view.getCellIndex(n);
59059             var locked = cm.isLocked(newIndex);
59060             if(pt == "after"){
59061                 newIndex++;
59062             }
59063             if(oldIndex < newIndex){
59064                 newIndex--;
59065             }
59066             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59067                 return false;
59068             }
59069             cm.setLocked(oldIndex, locked, true);
59070             cm.moveColumn(oldIndex, newIndex);
59071             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59072             return true;
59073         }
59074         return false;
59075     }
59076 });
59077 /*
59078  * Based on:
59079  * Ext JS Library 1.1.1
59080  * Copyright(c) 2006-2007, Ext JS, LLC.
59081  *
59082  * Originally Released Under LGPL - original licence link has changed is not relivant.
59083  *
59084  * Fork - LGPL
59085  * <script type="text/javascript">
59086  */
59087   
59088 /**
59089  * @class Roo.grid.GridView
59090  * @extends Roo.util.Observable
59091  *
59092  * @constructor
59093  * @param {Object} config
59094  */
59095 Roo.grid.GridView = function(config){
59096     Roo.grid.GridView.superclass.constructor.call(this);
59097     this.el = null;
59098
59099     Roo.apply(this, config);
59100 };
59101
59102 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59103
59104     unselectable :  'unselectable="on"',
59105     unselectableCls :  'x-unselectable',
59106     
59107     
59108     rowClass : "x-grid-row",
59109
59110     cellClass : "x-grid-col",
59111
59112     tdClass : "x-grid-td",
59113
59114     hdClass : "x-grid-hd",
59115
59116     splitClass : "x-grid-split",
59117
59118     sortClasses : ["sort-asc", "sort-desc"],
59119
59120     enableMoveAnim : false,
59121
59122     hlColor: "C3DAF9",
59123
59124     dh : Roo.DomHelper,
59125
59126     fly : Roo.Element.fly,
59127
59128     css : Roo.util.CSS,
59129
59130     borderWidth: 1,
59131
59132     splitOffset: 3,
59133
59134     scrollIncrement : 22,
59135
59136     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59137
59138     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59139
59140     bind : function(ds, cm){
59141         if(this.ds){
59142             this.ds.un("load", this.onLoad, this);
59143             this.ds.un("datachanged", this.onDataChange, this);
59144             this.ds.un("add", this.onAdd, this);
59145             this.ds.un("remove", this.onRemove, this);
59146             this.ds.un("update", this.onUpdate, this);
59147             this.ds.un("clear", this.onClear, this);
59148         }
59149         if(ds){
59150             ds.on("load", this.onLoad, this);
59151             ds.on("datachanged", this.onDataChange, this);
59152             ds.on("add", this.onAdd, this);
59153             ds.on("remove", this.onRemove, this);
59154             ds.on("update", this.onUpdate, this);
59155             ds.on("clear", this.onClear, this);
59156         }
59157         this.ds = ds;
59158
59159         if(this.cm){
59160             this.cm.un("widthchange", this.onColWidthChange, this);
59161             this.cm.un("headerchange", this.onHeaderChange, this);
59162             this.cm.un("hiddenchange", this.onHiddenChange, this);
59163             this.cm.un("columnmoved", this.onColumnMove, this);
59164             this.cm.un("columnlockchange", this.onColumnLock, this);
59165         }
59166         if(cm){
59167             this.generateRules(cm);
59168             cm.on("widthchange", this.onColWidthChange, this);
59169             cm.on("headerchange", this.onHeaderChange, this);
59170             cm.on("hiddenchange", this.onHiddenChange, this);
59171             cm.on("columnmoved", this.onColumnMove, this);
59172             cm.on("columnlockchange", this.onColumnLock, this);
59173         }
59174         this.cm = cm;
59175     },
59176
59177     init: function(grid){
59178         Roo.grid.GridView.superclass.init.call(this, grid);
59179
59180         this.bind(grid.dataSource, grid.colModel);
59181
59182         grid.on("headerclick", this.handleHeaderClick, this);
59183
59184         if(grid.trackMouseOver){
59185             grid.on("mouseover", this.onRowOver, this);
59186             grid.on("mouseout", this.onRowOut, this);
59187         }
59188         grid.cancelTextSelection = function(){};
59189         this.gridId = grid.id;
59190
59191         var tpls = this.templates || {};
59192
59193         if(!tpls.master){
59194             tpls.master = new Roo.Template(
59195                '<div class="x-grid" hidefocus="true">',
59196                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59197                   '<div class="x-grid-topbar"></div>',
59198                   '<div class="x-grid-scroller"><div></div></div>',
59199                   '<div class="x-grid-locked">',
59200                       '<div class="x-grid-header">{lockedHeader}</div>',
59201                       '<div class="x-grid-body">{lockedBody}</div>',
59202                   "</div>",
59203                   '<div class="x-grid-viewport">',
59204                       '<div class="x-grid-header">{header}</div>',
59205                       '<div class="x-grid-body">{body}</div>',
59206                   "</div>",
59207                   '<div class="x-grid-bottombar"></div>',
59208                  
59209                   '<div class="x-grid-resize-proxy">&#160;</div>',
59210                "</div>"
59211             );
59212             tpls.master.disableformats = true;
59213         }
59214
59215         if(!tpls.header){
59216             tpls.header = new Roo.Template(
59217                '<table border="0" cellspacing="0" cellpadding="0">',
59218                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59219                "</table>{splits}"
59220             );
59221             tpls.header.disableformats = true;
59222         }
59223         tpls.header.compile();
59224
59225         if(!tpls.hcell){
59226             tpls.hcell = new Roo.Template(
59227                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59228                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59229                 "</div></td>"
59230              );
59231              tpls.hcell.disableFormats = true;
59232         }
59233         tpls.hcell.compile();
59234
59235         if(!tpls.hsplit){
59236             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59237                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59238             tpls.hsplit.disableFormats = true;
59239         }
59240         tpls.hsplit.compile();
59241
59242         if(!tpls.body){
59243             tpls.body = new Roo.Template(
59244                '<table border="0" cellspacing="0" cellpadding="0">',
59245                "<tbody>{rows}</tbody>",
59246                "</table>"
59247             );
59248             tpls.body.disableFormats = true;
59249         }
59250         tpls.body.compile();
59251
59252         if(!tpls.row){
59253             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59254             tpls.row.disableFormats = true;
59255         }
59256         tpls.row.compile();
59257
59258         if(!tpls.cell){
59259             tpls.cell = new Roo.Template(
59260                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59261                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59262                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59263                 "</td>"
59264             );
59265             tpls.cell.disableFormats = true;
59266         }
59267         tpls.cell.compile();
59268
59269         this.templates = tpls;
59270     },
59271
59272     // remap these for backwards compat
59273     onColWidthChange : function(){
59274         this.updateColumns.apply(this, arguments);
59275     },
59276     onHeaderChange : function(){
59277         this.updateHeaders.apply(this, arguments);
59278     }, 
59279     onHiddenChange : function(){
59280         this.handleHiddenChange.apply(this, arguments);
59281     },
59282     onColumnMove : function(){
59283         this.handleColumnMove.apply(this, arguments);
59284     },
59285     onColumnLock : function(){
59286         this.handleLockChange.apply(this, arguments);
59287     },
59288
59289     onDataChange : function(){
59290         this.refresh();
59291         this.updateHeaderSortState();
59292     },
59293
59294     onClear : function(){
59295         this.refresh();
59296     },
59297
59298     onUpdate : function(ds, record){
59299         this.refreshRow(record);
59300     },
59301
59302     refreshRow : function(record){
59303         var ds = this.ds, index;
59304         if(typeof record == 'number'){
59305             index = record;
59306             record = ds.getAt(index);
59307         }else{
59308             index = ds.indexOf(record);
59309         }
59310         this.insertRows(ds, index, index, true);
59311         this.onRemove(ds, record, index+1, true);
59312         this.syncRowHeights(index, index);
59313         this.layout();
59314         this.fireEvent("rowupdated", this, index, record);
59315     },
59316
59317     onAdd : function(ds, records, index){
59318         this.insertRows(ds, index, index + (records.length-1));
59319     },
59320
59321     onRemove : function(ds, record, index, isUpdate){
59322         if(isUpdate !== true){
59323             this.fireEvent("beforerowremoved", this, index, record);
59324         }
59325         var bt = this.getBodyTable(), lt = this.getLockedTable();
59326         if(bt.rows[index]){
59327             bt.firstChild.removeChild(bt.rows[index]);
59328         }
59329         if(lt.rows[index]){
59330             lt.firstChild.removeChild(lt.rows[index]);
59331         }
59332         if(isUpdate !== true){
59333             this.stripeRows(index);
59334             this.syncRowHeights(index, index);
59335             this.layout();
59336             this.fireEvent("rowremoved", this, index, record);
59337         }
59338     },
59339
59340     onLoad : function(){
59341         this.scrollToTop();
59342     },
59343
59344     /**
59345      * Scrolls the grid to the top
59346      */
59347     scrollToTop : function(){
59348         if(this.scroller){
59349             this.scroller.dom.scrollTop = 0;
59350             this.syncScroll();
59351         }
59352     },
59353
59354     /**
59355      * Gets a panel in the header of the grid that can be used for toolbars etc.
59356      * After modifying the contents of this panel a call to grid.autoSize() may be
59357      * required to register any changes in size.
59358      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59359      * @return Roo.Element
59360      */
59361     getHeaderPanel : function(doShow){
59362         if(doShow){
59363             this.headerPanel.show();
59364         }
59365         return this.headerPanel;
59366     },
59367
59368     /**
59369      * Gets a panel in the footer 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 footer is hidden. Pass true to show the panel
59373      * @return Roo.Element
59374      */
59375     getFooterPanel : function(doShow){
59376         if(doShow){
59377             this.footerPanel.show();
59378         }
59379         return this.footerPanel;
59380     },
59381
59382     initElements : function(){
59383         var E = Roo.Element;
59384         var el = this.grid.getGridEl().dom.firstChild;
59385         var cs = el.childNodes;
59386
59387         this.el = new E(el);
59388         
59389          this.focusEl = new E(el.firstChild);
59390         this.focusEl.swallowEvent("click", true);
59391         
59392         this.headerPanel = new E(cs[1]);
59393         this.headerPanel.enableDisplayMode("block");
59394
59395         this.scroller = new E(cs[2]);
59396         this.scrollSizer = new E(this.scroller.dom.firstChild);
59397
59398         this.lockedWrap = new E(cs[3]);
59399         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59400         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59401
59402         this.mainWrap = new E(cs[4]);
59403         this.mainHd = new E(this.mainWrap.dom.firstChild);
59404         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59405
59406         this.footerPanel = new E(cs[5]);
59407         this.footerPanel.enableDisplayMode("block");
59408
59409         this.resizeProxy = new E(cs[6]);
59410
59411         this.headerSelector = String.format(
59412            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59413            this.lockedHd.id, this.mainHd.id
59414         );
59415
59416         this.splitterSelector = String.format(
59417            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59418            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59419         );
59420     },
59421     idToCssName : function(s)
59422     {
59423         return s.replace(/[^a-z0-9]+/ig, '-');
59424     },
59425
59426     getHeaderCell : function(index){
59427         return Roo.DomQuery.select(this.headerSelector)[index];
59428     },
59429
59430     getHeaderCellMeasure : function(index){
59431         return this.getHeaderCell(index).firstChild;
59432     },
59433
59434     getHeaderCellText : function(index){
59435         return this.getHeaderCell(index).firstChild.firstChild;
59436     },
59437
59438     getLockedTable : function(){
59439         return this.lockedBody.dom.firstChild;
59440     },
59441
59442     getBodyTable : function(){
59443         return this.mainBody.dom.firstChild;
59444     },
59445
59446     getLockedRow : function(index){
59447         return this.getLockedTable().rows[index];
59448     },
59449
59450     getRow : function(index){
59451         return this.getBodyTable().rows[index];
59452     },
59453
59454     getRowComposite : function(index){
59455         if(!this.rowEl){
59456             this.rowEl = new Roo.CompositeElementLite();
59457         }
59458         var els = [], lrow, mrow;
59459         if(lrow = this.getLockedRow(index)){
59460             els.push(lrow);
59461         }
59462         if(mrow = this.getRow(index)){
59463             els.push(mrow);
59464         }
59465         this.rowEl.elements = els;
59466         return this.rowEl;
59467     },
59468     /**
59469      * Gets the 'td' of the cell
59470      * 
59471      * @param {Integer} rowIndex row to select
59472      * @param {Integer} colIndex column to select
59473      * 
59474      * @return {Object} 
59475      */
59476     getCell : function(rowIndex, colIndex){
59477         var locked = this.cm.getLockedCount();
59478         var source;
59479         if(colIndex < locked){
59480             source = this.lockedBody.dom.firstChild;
59481         }else{
59482             source = this.mainBody.dom.firstChild;
59483             colIndex -= locked;
59484         }
59485         return source.rows[rowIndex].childNodes[colIndex];
59486     },
59487
59488     getCellText : function(rowIndex, colIndex){
59489         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59490     },
59491
59492     getCellBox : function(cell){
59493         var b = this.fly(cell).getBox();
59494         if(Roo.isOpera){ // opera fails to report the Y
59495             b.y = cell.offsetTop + this.mainBody.getY();
59496         }
59497         return b;
59498     },
59499
59500     getCellIndex : function(cell){
59501         var id = String(cell.className).match(this.cellRE);
59502         if(id){
59503             return parseInt(id[1], 10);
59504         }
59505         return 0;
59506     },
59507
59508     findHeaderIndex : function(n){
59509         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59510         return r ? this.getCellIndex(r) : false;
59511     },
59512
59513     findHeaderCell : function(n){
59514         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59515         return r ? r : false;
59516     },
59517
59518     findRowIndex : function(n){
59519         if(!n){
59520             return false;
59521         }
59522         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59523         return r ? r.rowIndex : false;
59524     },
59525
59526     findCellIndex : function(node){
59527         var stop = this.el.dom;
59528         while(node && node != stop){
59529             if(this.findRE.test(node.className)){
59530                 return this.getCellIndex(node);
59531             }
59532             node = node.parentNode;
59533         }
59534         return false;
59535     },
59536
59537     getColumnId : function(index){
59538         return this.cm.getColumnId(index);
59539     },
59540
59541     getSplitters : function()
59542     {
59543         if(this.splitterSelector){
59544            return Roo.DomQuery.select(this.splitterSelector);
59545         }else{
59546             return null;
59547       }
59548     },
59549
59550     getSplitter : function(index){
59551         return this.getSplitters()[index];
59552     },
59553
59554     onRowOver : function(e, t){
59555         var row;
59556         if((row = this.findRowIndex(t)) !== false){
59557             this.getRowComposite(row).addClass("x-grid-row-over");
59558         }
59559     },
59560
59561     onRowOut : function(e, t){
59562         var row;
59563         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59564             this.getRowComposite(row).removeClass("x-grid-row-over");
59565         }
59566     },
59567
59568     renderHeaders : function(){
59569         var cm = this.cm;
59570         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59571         var cb = [], lb = [], sb = [], lsb = [], p = {};
59572         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59573             p.cellId = "x-grid-hd-0-" + i;
59574             p.splitId = "x-grid-csplit-0-" + i;
59575             p.id = cm.getColumnId(i);
59576             p.value = cm.getColumnHeader(i) || "";
59577             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59578             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59579             if(!cm.isLocked(i)){
59580                 cb[cb.length] = ct.apply(p);
59581                 sb[sb.length] = st.apply(p);
59582             }else{
59583                 lb[lb.length] = ct.apply(p);
59584                 lsb[lsb.length] = st.apply(p);
59585             }
59586         }
59587         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59588                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59589     },
59590
59591     updateHeaders : function(){
59592         var html = this.renderHeaders();
59593         this.lockedHd.update(html[0]);
59594         this.mainHd.update(html[1]);
59595     },
59596
59597     /**
59598      * Focuses the specified row.
59599      * @param {Number} row The row index
59600      */
59601     focusRow : function(row)
59602     {
59603         //Roo.log('GridView.focusRow');
59604         var x = this.scroller.dom.scrollLeft;
59605         this.focusCell(row, 0, false);
59606         this.scroller.dom.scrollLeft = x;
59607     },
59608
59609     /**
59610      * Focuses the specified cell.
59611      * @param {Number} row The row index
59612      * @param {Number} col The column index
59613      * @param {Boolean} hscroll false to disable horizontal scrolling
59614      */
59615     focusCell : function(row, col, hscroll)
59616     {
59617         //Roo.log('GridView.focusCell');
59618         var el = this.ensureVisible(row, col, hscroll);
59619         this.focusEl.alignTo(el, "tl-tl");
59620         if(Roo.isGecko){
59621             this.focusEl.focus();
59622         }else{
59623             this.focusEl.focus.defer(1, this.focusEl);
59624         }
59625     },
59626
59627     /**
59628      * Scrolls the specified cell into view
59629      * @param {Number} row The row index
59630      * @param {Number} col The column index
59631      * @param {Boolean} hscroll false to disable horizontal scrolling
59632      */
59633     ensureVisible : function(row, col, hscroll)
59634     {
59635         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59636         //return null; //disable for testing.
59637         if(typeof row != "number"){
59638             row = row.rowIndex;
59639         }
59640         if(row < 0 && row >= this.ds.getCount()){
59641             return  null;
59642         }
59643         col = (col !== undefined ? col : 0);
59644         var cm = this.grid.colModel;
59645         while(cm.isHidden(col)){
59646             col++;
59647         }
59648
59649         var el = this.getCell(row, col);
59650         if(!el){
59651             return null;
59652         }
59653         var c = this.scroller.dom;
59654
59655         var ctop = parseInt(el.offsetTop, 10);
59656         var cleft = parseInt(el.offsetLeft, 10);
59657         var cbot = ctop + el.offsetHeight;
59658         var cright = cleft + el.offsetWidth;
59659         
59660         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59661         var stop = parseInt(c.scrollTop, 10);
59662         var sleft = parseInt(c.scrollLeft, 10);
59663         var sbot = stop + ch;
59664         var sright = sleft + c.clientWidth;
59665         /*
59666         Roo.log('GridView.ensureVisible:' +
59667                 ' ctop:' + ctop +
59668                 ' c.clientHeight:' + c.clientHeight +
59669                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59670                 ' stop:' + stop +
59671                 ' cbot:' + cbot +
59672                 ' sbot:' + sbot +
59673                 ' ch:' + ch  
59674                 );
59675         */
59676         if(ctop < stop){
59677             c.scrollTop = ctop;
59678             //Roo.log("set scrolltop to ctop DISABLE?");
59679         }else if(cbot > sbot){
59680             //Roo.log("set scrolltop to cbot-ch");
59681             c.scrollTop = cbot-ch;
59682         }
59683         
59684         if(hscroll !== false){
59685             if(cleft < sleft){
59686                 c.scrollLeft = cleft;
59687             }else if(cright > sright){
59688                 c.scrollLeft = cright-c.clientWidth;
59689             }
59690         }
59691          
59692         return el;
59693     },
59694
59695     updateColumns : function(){
59696         this.grid.stopEditing();
59697         var cm = this.grid.colModel, colIds = this.getColumnIds();
59698         //var totalWidth = cm.getTotalWidth();
59699         var pos = 0;
59700         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59701             //if(cm.isHidden(i)) continue;
59702             var w = cm.getColumnWidth(i);
59703             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59704             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59705         }
59706         this.updateSplitters();
59707     },
59708
59709     generateRules : function(cm){
59710         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59711         Roo.util.CSS.removeStyleSheet(rulesId);
59712         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59713             var cid = cm.getColumnId(i);
59714             var align = '';
59715             if(cm.config[i].align){
59716                 align = 'text-align:'+cm.config[i].align+';';
59717             }
59718             var hidden = '';
59719             if(cm.isHidden(i)){
59720                 hidden = 'display:none;';
59721             }
59722             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59723             ruleBuf.push(
59724                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59725                     this.hdSelector, cid, " {\n", align, width, "}\n",
59726                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59727                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59728         }
59729         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59730     },
59731
59732     updateSplitters : function(){
59733         var cm = this.cm, s = this.getSplitters();
59734         if(s){ // splitters not created yet
59735             var pos = 0, locked = true;
59736             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59737                 if(cm.isHidden(i)) {
59738                     continue;
59739                 }
59740                 var w = cm.getColumnWidth(i); // make sure it's a number
59741                 if(!cm.isLocked(i) && locked){
59742                     pos = 0;
59743                     locked = false;
59744                 }
59745                 pos += w;
59746                 s[i].style.left = (pos-this.splitOffset) + "px";
59747             }
59748         }
59749     },
59750
59751     handleHiddenChange : function(colModel, colIndex, hidden){
59752         if(hidden){
59753             this.hideColumn(colIndex);
59754         }else{
59755             this.unhideColumn(colIndex);
59756         }
59757     },
59758
59759     hideColumn : function(colIndex){
59760         var cid = this.getColumnId(colIndex);
59761         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59762         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59763         if(Roo.isSafari){
59764             this.updateHeaders();
59765         }
59766         this.updateSplitters();
59767         this.layout();
59768     },
59769
59770     unhideColumn : function(colIndex){
59771         var cid = this.getColumnId(colIndex);
59772         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59773         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59774
59775         if(Roo.isSafari){
59776             this.updateHeaders();
59777         }
59778         this.updateSplitters();
59779         this.layout();
59780     },
59781
59782     insertRows : function(dm, firstRow, lastRow, isUpdate){
59783         if(firstRow == 0 && lastRow == dm.getCount()-1){
59784             this.refresh();
59785         }else{
59786             if(!isUpdate){
59787                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59788             }
59789             var s = this.getScrollState();
59790             var markup = this.renderRows(firstRow, lastRow);
59791             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59792             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59793             this.restoreScroll(s);
59794             if(!isUpdate){
59795                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59796                 this.syncRowHeights(firstRow, lastRow);
59797                 this.stripeRows(firstRow);
59798                 this.layout();
59799             }
59800         }
59801     },
59802
59803     bufferRows : function(markup, target, index){
59804         var before = null, trows = target.rows, tbody = target.tBodies[0];
59805         if(index < trows.length){
59806             before = trows[index];
59807         }
59808         var b = document.createElement("div");
59809         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59810         var rows = b.firstChild.rows;
59811         for(var i = 0, len = rows.length; i < len; i++){
59812             if(before){
59813                 tbody.insertBefore(rows[0], before);
59814             }else{
59815                 tbody.appendChild(rows[0]);
59816             }
59817         }
59818         b.innerHTML = "";
59819         b = null;
59820     },
59821
59822     deleteRows : function(dm, firstRow, lastRow){
59823         if(dm.getRowCount()<1){
59824             this.fireEvent("beforerefresh", this);
59825             this.mainBody.update("");
59826             this.lockedBody.update("");
59827             this.fireEvent("refresh", this);
59828         }else{
59829             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59830             var bt = this.getBodyTable();
59831             var tbody = bt.firstChild;
59832             var rows = bt.rows;
59833             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59834                 tbody.removeChild(rows[firstRow]);
59835             }
59836             this.stripeRows(firstRow);
59837             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59838         }
59839     },
59840
59841     updateRows : function(dataSource, firstRow, lastRow){
59842         var s = this.getScrollState();
59843         this.refresh();
59844         this.restoreScroll(s);
59845     },
59846
59847     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59848         if(!noRefresh){
59849            this.refresh();
59850         }
59851         this.updateHeaderSortState();
59852     },
59853
59854     getScrollState : function(){
59855         
59856         var sb = this.scroller.dom;
59857         return {left: sb.scrollLeft, top: sb.scrollTop};
59858     },
59859
59860     stripeRows : function(startRow){
59861         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59862             return;
59863         }
59864         startRow = startRow || 0;
59865         var rows = this.getBodyTable().rows;
59866         var lrows = this.getLockedTable().rows;
59867         var cls = ' x-grid-row-alt ';
59868         for(var i = startRow, len = rows.length; i < len; i++){
59869             var row = rows[i], lrow = lrows[i];
59870             var isAlt = ((i+1) % 2 == 0);
59871             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59872             if(isAlt == hasAlt){
59873                 continue;
59874             }
59875             if(isAlt){
59876                 row.className += " x-grid-row-alt";
59877             }else{
59878                 row.className = row.className.replace("x-grid-row-alt", "");
59879             }
59880             if(lrow){
59881                 lrow.className = row.className;
59882             }
59883         }
59884     },
59885
59886     restoreScroll : function(state){
59887         //Roo.log('GridView.restoreScroll');
59888         var sb = this.scroller.dom;
59889         sb.scrollLeft = state.left;
59890         sb.scrollTop = state.top;
59891         this.syncScroll();
59892     },
59893
59894     syncScroll : function(){
59895         //Roo.log('GridView.syncScroll');
59896         var sb = this.scroller.dom;
59897         var sh = this.mainHd.dom;
59898         var bs = this.mainBody.dom;
59899         var lv = this.lockedBody.dom;
59900         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59901         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59902     },
59903
59904     handleScroll : function(e){
59905         this.syncScroll();
59906         var sb = this.scroller.dom;
59907         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59908         e.stopEvent();
59909     },
59910
59911     handleWheel : function(e){
59912         var d = e.getWheelDelta();
59913         this.scroller.dom.scrollTop -= d*22;
59914         // set this here to prevent jumpy scrolling on large tables
59915         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59916         e.stopEvent();
59917     },
59918
59919     renderRows : function(startRow, endRow){
59920         // pull in all the crap needed to render rows
59921         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59922         var colCount = cm.getColumnCount();
59923
59924         if(ds.getCount() < 1){
59925             return ["", ""];
59926         }
59927
59928         // build a map for all the columns
59929         var cs = [];
59930         for(var i = 0; i < colCount; i++){
59931             var name = cm.getDataIndex(i);
59932             cs[i] = {
59933                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59934                 renderer : cm.getRenderer(i),
59935                 id : cm.getColumnId(i),
59936                 locked : cm.isLocked(i),
59937                 has_editor : cm.isCellEditable(i)
59938             };
59939         }
59940
59941         startRow = startRow || 0;
59942         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59943
59944         // records to render
59945         var rs = ds.getRange(startRow, endRow);
59946
59947         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59948     },
59949
59950     // As much as I hate to duplicate code, this was branched because FireFox really hates
59951     // [].join("") on strings. The performance difference was substantial enough to
59952     // branch this function
59953     doRender : Roo.isGecko ?
59954             function(cs, rs, ds, startRow, colCount, stripe){
59955                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59956                 // buffers
59957                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59958                 
59959                 var hasListener = this.grid.hasListener('rowclass');
59960                 var rowcfg = {};
59961                 for(var j = 0, len = rs.length; j < len; j++){
59962                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59963                     for(var i = 0; i < colCount; i++){
59964                         c = cs[i];
59965                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59966                         p.id = c.id;
59967                         p.css = p.attr = "";
59968                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59969                         if(p.value == undefined || p.value === "") {
59970                             p.value = "&#160;";
59971                         }
59972                         if(c.has_editor){
59973                             p.css += ' x-grid-editable-cell';
59974                         }
59975                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59976                             p.css +=  ' x-grid-dirty-cell';
59977                         }
59978                         var markup = ct.apply(p);
59979                         if(!c.locked){
59980                             cb+= markup;
59981                         }else{
59982                             lcb+= markup;
59983                         }
59984                     }
59985                     var alt = [];
59986                     if(stripe && ((rowIndex+1) % 2 == 0)){
59987                         alt.push("x-grid-row-alt")
59988                     }
59989                     if(r.dirty){
59990                         alt.push(  " x-grid-dirty-row");
59991                     }
59992                     rp.cells = lcb;
59993                     if(this.getRowClass){
59994                         alt.push(this.getRowClass(r, rowIndex));
59995                     }
59996                     if (hasListener) {
59997                         rowcfg = {
59998                              
59999                             record: r,
60000                             rowIndex : rowIndex,
60001                             rowClass : ''
60002                         };
60003                         this.grid.fireEvent('rowclass', this, rowcfg);
60004                         alt.push(rowcfg.rowClass);
60005                     }
60006                     rp.alt = alt.join(" ");
60007                     lbuf+= rt.apply(rp);
60008                     rp.cells = cb;
60009                     buf+=  rt.apply(rp);
60010                 }
60011                 return [lbuf, buf];
60012             } :
60013             function(cs, rs, ds, startRow, colCount, stripe){
60014                 var ts = this.templates, ct = ts.cell, rt = ts.row;
60015                 // buffers
60016                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60017                 var hasListener = this.grid.hasListener('rowclass');
60018  
60019                 var rowcfg = {};
60020                 for(var j = 0, len = rs.length; j < len; j++){
60021                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60022                     for(var i = 0; i < colCount; i++){
60023                         c = cs[i];
60024                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60025                         p.id = c.id;
60026                         p.css = p.attr = "";
60027                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60028                         if(p.value == undefined || p.value === "") {
60029                             p.value = "&#160;";
60030                         }
60031                         //Roo.log(c);
60032                          if(c.has_editor){
60033                             p.css += ' x-grid-editable-cell';
60034                         }
60035                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60036                             p.css += ' x-grid-dirty-cell' 
60037                         }
60038                         
60039                         var markup = ct.apply(p);
60040                         if(!c.locked){
60041                             cb[cb.length] = markup;
60042                         }else{
60043                             lcb[lcb.length] = markup;
60044                         }
60045                     }
60046                     var alt = [];
60047                     if(stripe && ((rowIndex+1) % 2 == 0)){
60048                         alt.push( "x-grid-row-alt");
60049                     }
60050                     if(r.dirty){
60051                         alt.push(" x-grid-dirty-row");
60052                     }
60053                     rp.cells = lcb;
60054                     if(this.getRowClass){
60055                         alt.push( this.getRowClass(r, rowIndex));
60056                     }
60057                     if (hasListener) {
60058                         rowcfg = {
60059                              
60060                             record: r,
60061                             rowIndex : rowIndex,
60062                             rowClass : ''
60063                         };
60064                         this.grid.fireEvent('rowclass', this, rowcfg);
60065                         alt.push(rowcfg.rowClass);
60066                     }
60067                     
60068                     rp.alt = alt.join(" ");
60069                     rp.cells = lcb.join("");
60070                     lbuf[lbuf.length] = rt.apply(rp);
60071                     rp.cells = cb.join("");
60072                     buf[buf.length] =  rt.apply(rp);
60073                 }
60074                 return [lbuf.join(""), buf.join("")];
60075             },
60076
60077     renderBody : function(){
60078         var markup = this.renderRows();
60079         var bt = this.templates.body;
60080         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60081     },
60082
60083     /**
60084      * Refreshes the grid
60085      * @param {Boolean} headersToo
60086      */
60087     refresh : function(headersToo){
60088         this.fireEvent("beforerefresh", this);
60089         this.grid.stopEditing();
60090         var result = this.renderBody();
60091         this.lockedBody.update(result[0]);
60092         this.mainBody.update(result[1]);
60093         if(headersToo === true){
60094             this.updateHeaders();
60095             this.updateColumns();
60096             this.updateSplitters();
60097             this.updateHeaderSortState();
60098         }
60099         this.syncRowHeights();
60100         this.layout();
60101         this.fireEvent("refresh", this);
60102     },
60103
60104     handleColumnMove : function(cm, oldIndex, newIndex){
60105         this.indexMap = null;
60106         var s = this.getScrollState();
60107         this.refresh(true);
60108         this.restoreScroll(s);
60109         this.afterMove(newIndex);
60110     },
60111
60112     afterMove : function(colIndex){
60113         if(this.enableMoveAnim && Roo.enableFx){
60114             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60115         }
60116         // if multisort - fix sortOrder, and reload..
60117         if (this.grid.dataSource.multiSort) {
60118             // the we can call sort again..
60119             var dm = this.grid.dataSource;
60120             var cm = this.grid.colModel;
60121             var so = [];
60122             for(var i = 0; i < cm.config.length; i++ ) {
60123                 
60124                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60125                     continue; // dont' bother, it's not in sort list or being set.
60126                 }
60127                 
60128                 so.push(cm.config[i].dataIndex);
60129             };
60130             dm.sortOrder = so;
60131             dm.load(dm.lastOptions);
60132             
60133             
60134         }
60135         
60136     },
60137
60138     updateCell : function(dm, rowIndex, dataIndex){
60139         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60140         if(typeof colIndex == "undefined"){ // not present in grid
60141             return;
60142         }
60143         var cm = this.grid.colModel;
60144         var cell = this.getCell(rowIndex, colIndex);
60145         var cellText = this.getCellText(rowIndex, colIndex);
60146
60147         var p = {
60148             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60149             id : cm.getColumnId(colIndex),
60150             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60151         };
60152         var renderer = cm.getRenderer(colIndex);
60153         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60154         if(typeof val == "undefined" || val === "") {
60155             val = "&#160;";
60156         }
60157         cellText.innerHTML = val;
60158         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60159         this.syncRowHeights(rowIndex, rowIndex);
60160     },
60161
60162     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60163         var maxWidth = 0;
60164         if(this.grid.autoSizeHeaders){
60165             var h = this.getHeaderCellMeasure(colIndex);
60166             maxWidth = Math.max(maxWidth, h.scrollWidth);
60167         }
60168         var tb, index;
60169         if(this.cm.isLocked(colIndex)){
60170             tb = this.getLockedTable();
60171             index = colIndex;
60172         }else{
60173             tb = this.getBodyTable();
60174             index = colIndex - this.cm.getLockedCount();
60175         }
60176         if(tb && tb.rows){
60177             var rows = tb.rows;
60178             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60179             for(var i = 0; i < stopIndex; i++){
60180                 var cell = rows[i].childNodes[index].firstChild;
60181                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60182             }
60183         }
60184         return maxWidth + /*margin for error in IE*/ 5;
60185     },
60186     /**
60187      * Autofit a column to its content.
60188      * @param {Number} colIndex
60189      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60190      */
60191      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60192          if(this.cm.isHidden(colIndex)){
60193              return; // can't calc a hidden column
60194          }
60195         if(forceMinSize){
60196             var cid = this.cm.getColumnId(colIndex);
60197             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60198            if(this.grid.autoSizeHeaders){
60199                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60200            }
60201         }
60202         var newWidth = this.calcColumnWidth(colIndex);
60203         this.cm.setColumnWidth(colIndex,
60204             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60205         if(!suppressEvent){
60206             this.grid.fireEvent("columnresize", colIndex, newWidth);
60207         }
60208     },
60209
60210     /**
60211      * Autofits all columns to their content and then expands to fit any extra space in the grid
60212      */
60213      autoSizeColumns : function(){
60214         var cm = this.grid.colModel;
60215         var colCount = cm.getColumnCount();
60216         for(var i = 0; i < colCount; i++){
60217             this.autoSizeColumn(i, true, true);
60218         }
60219         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60220             this.fitColumns();
60221         }else{
60222             this.updateColumns();
60223             this.layout();
60224         }
60225     },
60226
60227     /**
60228      * Autofits all columns to the grid's width proportionate with their current size
60229      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60230      */
60231     fitColumns : function(reserveScrollSpace){
60232         var cm = this.grid.colModel;
60233         var colCount = cm.getColumnCount();
60234         var cols = [];
60235         var width = 0;
60236         var i, w;
60237         for (i = 0; i < colCount; i++){
60238             if(!cm.isHidden(i) && !cm.isFixed(i)){
60239                 w = cm.getColumnWidth(i);
60240                 cols.push(i);
60241                 cols.push(w);
60242                 width += w;
60243             }
60244         }
60245         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60246         if(reserveScrollSpace){
60247             avail -= 17;
60248         }
60249         var frac = (avail - cm.getTotalWidth())/width;
60250         while (cols.length){
60251             w = cols.pop();
60252             i = cols.pop();
60253             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60254         }
60255         this.updateColumns();
60256         this.layout();
60257     },
60258
60259     onRowSelect : function(rowIndex){
60260         var row = this.getRowComposite(rowIndex);
60261         row.addClass("x-grid-row-selected");
60262     },
60263
60264     onRowDeselect : function(rowIndex){
60265         var row = this.getRowComposite(rowIndex);
60266         row.removeClass("x-grid-row-selected");
60267     },
60268
60269     onCellSelect : function(row, col){
60270         var cell = this.getCell(row, col);
60271         if(cell){
60272             Roo.fly(cell).addClass("x-grid-cell-selected");
60273         }
60274     },
60275
60276     onCellDeselect : function(row, col){
60277         var cell = this.getCell(row, col);
60278         if(cell){
60279             Roo.fly(cell).removeClass("x-grid-cell-selected");
60280         }
60281     },
60282
60283     updateHeaderSortState : function(){
60284         
60285         // sort state can be single { field: xxx, direction : yyy}
60286         // or   { xxx=>ASC , yyy : DESC ..... }
60287         
60288         var mstate = {};
60289         if (!this.ds.multiSort) { 
60290             var state = this.ds.getSortState();
60291             if(!state){
60292                 return;
60293             }
60294             mstate[state.field] = state.direction;
60295             // FIXME... - this is not used here.. but might be elsewhere..
60296             this.sortState = state;
60297             
60298         } else {
60299             mstate = this.ds.sortToggle;
60300         }
60301         //remove existing sort classes..
60302         
60303         var sc = this.sortClasses;
60304         var hds = this.el.select(this.headerSelector).removeClass(sc);
60305         
60306         for(var f in mstate) {
60307         
60308             var sortColumn = this.cm.findColumnIndex(f);
60309             
60310             if(sortColumn != -1){
60311                 var sortDir = mstate[f];        
60312                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60313             }
60314         }
60315         
60316          
60317         
60318     },
60319
60320
60321     handleHeaderClick : function(g, index,e){
60322         
60323         Roo.log("header click");
60324         
60325         if (Roo.isTouch) {
60326             // touch events on header are handled by context
60327             this.handleHdCtx(g,index,e);
60328             return;
60329         }
60330         
60331         
60332         if(this.headersDisabled){
60333             return;
60334         }
60335         var dm = g.dataSource, cm = g.colModel;
60336         if(!cm.isSortable(index)){
60337             return;
60338         }
60339         g.stopEditing();
60340         
60341         if (dm.multiSort) {
60342             // update the sortOrder
60343             var so = [];
60344             for(var i = 0; i < cm.config.length; i++ ) {
60345                 
60346                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60347                     continue; // dont' bother, it's not in sort list or being set.
60348                 }
60349                 
60350                 so.push(cm.config[i].dataIndex);
60351             };
60352             dm.sortOrder = so;
60353         }
60354         
60355         
60356         dm.sort(cm.getDataIndex(index));
60357     },
60358
60359
60360     destroy : function(){
60361         if(this.colMenu){
60362             this.colMenu.removeAll();
60363             Roo.menu.MenuMgr.unregister(this.colMenu);
60364             this.colMenu.getEl().remove();
60365             delete this.colMenu;
60366         }
60367         if(this.hmenu){
60368             this.hmenu.removeAll();
60369             Roo.menu.MenuMgr.unregister(this.hmenu);
60370             this.hmenu.getEl().remove();
60371             delete this.hmenu;
60372         }
60373         if(this.grid.enableColumnMove){
60374             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60375             if(dds){
60376                 for(var dd in dds){
60377                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60378                         var elid = dds[dd].dragElId;
60379                         dds[dd].unreg();
60380                         Roo.get(elid).remove();
60381                     } else if(dds[dd].config.isTarget){
60382                         dds[dd].proxyTop.remove();
60383                         dds[dd].proxyBottom.remove();
60384                         dds[dd].unreg();
60385                     }
60386                     if(Roo.dd.DDM.locationCache[dd]){
60387                         delete Roo.dd.DDM.locationCache[dd];
60388                     }
60389                 }
60390                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60391             }
60392         }
60393         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60394         this.bind(null, null);
60395         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60396     },
60397
60398     handleLockChange : function(){
60399         this.refresh(true);
60400     },
60401
60402     onDenyColumnLock : function(){
60403
60404     },
60405
60406     onDenyColumnHide : function(){
60407
60408     },
60409
60410     handleHdMenuClick : function(item){
60411         var index = this.hdCtxIndex;
60412         var cm = this.cm, ds = this.ds;
60413         switch(item.id){
60414             case "asc":
60415                 ds.sort(cm.getDataIndex(index), "ASC");
60416                 break;
60417             case "desc":
60418                 ds.sort(cm.getDataIndex(index), "DESC");
60419                 break;
60420             case "lock":
60421                 var lc = cm.getLockedCount();
60422                 if(cm.getColumnCount(true) <= lc+1){
60423                     this.onDenyColumnLock();
60424                     return;
60425                 }
60426                 if(lc != index){
60427                     cm.setLocked(index, true, true);
60428                     cm.moveColumn(index, lc);
60429                     this.grid.fireEvent("columnmove", index, lc);
60430                 }else{
60431                     cm.setLocked(index, true);
60432                 }
60433             break;
60434             case "unlock":
60435                 var lc = cm.getLockedCount();
60436                 if((lc-1) != index){
60437                     cm.setLocked(index, false, true);
60438                     cm.moveColumn(index, lc-1);
60439                     this.grid.fireEvent("columnmove", index, lc-1);
60440                 }else{
60441                     cm.setLocked(index, false);
60442                 }
60443             break;
60444             case 'wider': // used to expand cols on touch..
60445             case 'narrow':
60446                 var cw = cm.getColumnWidth(index);
60447                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60448                 cw = Math.max(0, cw);
60449                 cw = Math.min(cw,4000);
60450                 cm.setColumnWidth(index, cw);
60451                 break;
60452                 
60453             default:
60454                 index = cm.getIndexById(item.id.substr(4));
60455                 if(index != -1){
60456                     if(item.checked && cm.getColumnCount(true) <= 1){
60457                         this.onDenyColumnHide();
60458                         return false;
60459                     }
60460                     cm.setHidden(index, item.checked);
60461                 }
60462         }
60463         return true;
60464     },
60465
60466     beforeColMenuShow : function(){
60467         var cm = this.cm,  colCount = cm.getColumnCount();
60468         this.colMenu.removeAll();
60469         
60470         var items = [];
60471         for(var i = 0; i < colCount; i++){
60472             items.push({
60473                 id: "col-"+cm.getColumnId(i),
60474                 text: cm.getColumnHeader(i),
60475                 checked: !cm.isHidden(i),
60476                 hideOnClick:false
60477             });
60478         }
60479         
60480         if (this.grid.sortColMenu) {
60481             items.sort(function(a,b) {
60482                 if (a.text == b.text) {
60483                     return 0;
60484                 }
60485                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60486             });
60487         }
60488         
60489         for(var i = 0; i < colCount; i++){
60490             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60491         }
60492     },
60493
60494     handleHdCtx : function(g, index, e){
60495         e.stopEvent();
60496         var hd = this.getHeaderCell(index);
60497         this.hdCtxIndex = index;
60498         var ms = this.hmenu.items, cm = this.cm;
60499         ms.get("asc").setDisabled(!cm.isSortable(index));
60500         ms.get("desc").setDisabled(!cm.isSortable(index));
60501         if(this.grid.enableColLock !== false){
60502             ms.get("lock").setDisabled(cm.isLocked(index));
60503             ms.get("unlock").setDisabled(!cm.isLocked(index));
60504         }
60505         this.hmenu.show(hd, "tl-bl");
60506     },
60507
60508     handleHdOver : function(e){
60509         var hd = this.findHeaderCell(e.getTarget());
60510         if(hd && !this.headersDisabled){
60511             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60512                this.fly(hd).addClass("x-grid-hd-over");
60513             }
60514         }
60515     },
60516
60517     handleHdOut : function(e){
60518         var hd = this.findHeaderCell(e.getTarget());
60519         if(hd){
60520             this.fly(hd).removeClass("x-grid-hd-over");
60521         }
60522     },
60523
60524     handleSplitDblClick : function(e, t){
60525         var i = this.getCellIndex(t);
60526         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60527             this.autoSizeColumn(i, true);
60528             this.layout();
60529         }
60530     },
60531
60532     render : function(){
60533
60534         var cm = this.cm;
60535         var colCount = cm.getColumnCount();
60536
60537         if(this.grid.monitorWindowResize === true){
60538             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60539         }
60540         var header = this.renderHeaders();
60541         var body = this.templates.body.apply({rows:""});
60542         var html = this.templates.master.apply({
60543             lockedBody: body,
60544             body: body,
60545             lockedHeader: header[0],
60546             header: header[1]
60547         });
60548
60549         //this.updateColumns();
60550
60551         this.grid.getGridEl().dom.innerHTML = html;
60552
60553         this.initElements();
60554         
60555         // a kludge to fix the random scolling effect in webkit
60556         this.el.on("scroll", function() {
60557             this.el.dom.scrollTop=0; // hopefully not recursive..
60558         },this);
60559
60560         this.scroller.on("scroll", this.handleScroll, this);
60561         this.lockedBody.on("mousewheel", this.handleWheel, this);
60562         this.mainBody.on("mousewheel", this.handleWheel, this);
60563
60564         this.mainHd.on("mouseover", this.handleHdOver, this);
60565         this.mainHd.on("mouseout", this.handleHdOut, this);
60566         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60567                 {delegate: "."+this.splitClass});
60568
60569         this.lockedHd.on("mouseover", this.handleHdOver, this);
60570         this.lockedHd.on("mouseout", this.handleHdOut, this);
60571         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60572                 {delegate: "."+this.splitClass});
60573
60574         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60575             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60576         }
60577
60578         this.updateSplitters();
60579
60580         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60581             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60582             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60583         }
60584
60585         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60586             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60587             this.hmenu.add(
60588                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60589                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60590             );
60591             if(this.grid.enableColLock !== false){
60592                 this.hmenu.add('-',
60593                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60594                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60595                 );
60596             }
60597             if (Roo.isTouch) {
60598                  this.hmenu.add('-',
60599                     {id:"wider", text: this.columnsWiderText},
60600                     {id:"narrow", text: this.columnsNarrowText }
60601                 );
60602                 
60603                  
60604             }
60605             
60606             if(this.grid.enableColumnHide !== false){
60607
60608                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60609                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60610                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60611
60612                 this.hmenu.add('-',
60613                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60614                 );
60615             }
60616             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60617
60618             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60619         }
60620
60621         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60622             this.dd = new Roo.grid.GridDragZone(this.grid, {
60623                 ddGroup : this.grid.ddGroup || 'GridDD'
60624             });
60625             
60626         }
60627
60628         /*
60629         for(var i = 0; i < colCount; i++){
60630             if(cm.isHidden(i)){
60631                 this.hideColumn(i);
60632             }
60633             if(cm.config[i].align){
60634                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60635                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60636             }
60637         }*/
60638         
60639         this.updateHeaderSortState();
60640
60641         this.beforeInitialResize();
60642         this.layout(true);
60643
60644         // two part rendering gives faster view to the user
60645         this.renderPhase2.defer(1, this);
60646     },
60647
60648     renderPhase2 : function(){
60649         // render the rows now
60650         this.refresh();
60651         if(this.grid.autoSizeColumns){
60652             this.autoSizeColumns();
60653         }
60654     },
60655
60656     beforeInitialResize : function(){
60657
60658     },
60659
60660     onColumnSplitterMoved : function(i, w){
60661         this.userResized = true;
60662         var cm = this.grid.colModel;
60663         cm.setColumnWidth(i, w, true);
60664         var cid = cm.getColumnId(i);
60665         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60666         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60667         this.updateSplitters();
60668         this.layout();
60669         this.grid.fireEvent("columnresize", i, w);
60670     },
60671
60672     syncRowHeights : function(startIndex, endIndex){
60673         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60674             startIndex = startIndex || 0;
60675             var mrows = this.getBodyTable().rows;
60676             var lrows = this.getLockedTable().rows;
60677             var len = mrows.length-1;
60678             endIndex = Math.min(endIndex || len, len);
60679             for(var i = startIndex; i <= endIndex; i++){
60680                 var m = mrows[i], l = lrows[i];
60681                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60682                 m.style.height = l.style.height = h + "px";
60683             }
60684         }
60685     },
60686
60687     layout : function(initialRender, is2ndPass)
60688     {
60689         var g = this.grid;
60690         var auto = g.autoHeight;
60691         var scrollOffset = 16;
60692         var c = g.getGridEl(), cm = this.cm,
60693                 expandCol = g.autoExpandColumn,
60694                 gv = this;
60695         //c.beginMeasure();
60696
60697         if(!c.dom.offsetWidth){ // display:none?
60698             if(initialRender){
60699                 this.lockedWrap.show();
60700                 this.mainWrap.show();
60701             }
60702             return;
60703         }
60704
60705         var hasLock = this.cm.isLocked(0);
60706
60707         var tbh = this.headerPanel.getHeight();
60708         var bbh = this.footerPanel.getHeight();
60709
60710         if(auto){
60711             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60712             var newHeight = ch + c.getBorderWidth("tb");
60713             if(g.maxHeight){
60714                 newHeight = Math.min(g.maxHeight, newHeight);
60715             }
60716             c.setHeight(newHeight);
60717         }
60718
60719         if(g.autoWidth){
60720             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60721         }
60722
60723         var s = this.scroller;
60724
60725         var csize = c.getSize(true);
60726
60727         this.el.setSize(csize.width, csize.height);
60728
60729         this.headerPanel.setWidth(csize.width);
60730         this.footerPanel.setWidth(csize.width);
60731
60732         var hdHeight = this.mainHd.getHeight();
60733         var vw = csize.width;
60734         var vh = csize.height - (tbh + bbh);
60735
60736         s.setSize(vw, vh);
60737
60738         var bt = this.getBodyTable();
60739         
60740         if(cm.getLockedCount() == cm.config.length){
60741             bt = this.getLockedTable();
60742         }
60743         
60744         var ltWidth = hasLock ?
60745                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60746
60747         var scrollHeight = bt.offsetHeight;
60748         var scrollWidth = ltWidth + bt.offsetWidth;
60749         var vscroll = false, hscroll = false;
60750
60751         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60752
60753         var lw = this.lockedWrap, mw = this.mainWrap;
60754         var lb = this.lockedBody, mb = this.mainBody;
60755
60756         setTimeout(function(){
60757             var t = s.dom.offsetTop;
60758             var w = s.dom.clientWidth,
60759                 h = s.dom.clientHeight;
60760
60761             lw.setTop(t);
60762             lw.setSize(ltWidth, h);
60763
60764             mw.setLeftTop(ltWidth, t);
60765             mw.setSize(w-ltWidth, h);
60766
60767             lb.setHeight(h-hdHeight);
60768             mb.setHeight(h-hdHeight);
60769
60770             if(is2ndPass !== true && !gv.userResized && expandCol){
60771                 // high speed resize without full column calculation
60772                 
60773                 var ci = cm.getIndexById(expandCol);
60774                 if (ci < 0) {
60775                     ci = cm.findColumnIndex(expandCol);
60776                 }
60777                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60778                 var expandId = cm.getColumnId(ci);
60779                 var  tw = cm.getTotalWidth(false);
60780                 var currentWidth = cm.getColumnWidth(ci);
60781                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60782                 if(currentWidth != cw){
60783                     cm.setColumnWidth(ci, cw, true);
60784                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60785                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60786                     gv.updateSplitters();
60787                     gv.layout(false, true);
60788                 }
60789             }
60790
60791             if(initialRender){
60792                 lw.show();
60793                 mw.show();
60794             }
60795             //c.endMeasure();
60796         }, 10);
60797     },
60798
60799     onWindowResize : function(){
60800         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60801             return;
60802         }
60803         this.layout();
60804     },
60805
60806     appendFooter : function(parentEl){
60807         return null;
60808     },
60809
60810     sortAscText : "Sort Ascending",
60811     sortDescText : "Sort Descending",
60812     lockText : "Lock Column",
60813     unlockText : "Unlock Column",
60814     columnsText : "Columns",
60815  
60816     columnsWiderText : "Wider",
60817     columnsNarrowText : "Thinner"
60818 });
60819
60820
60821 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60822     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60823     this.proxy.el.addClass('x-grid3-col-dd');
60824 };
60825
60826 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60827     handleMouseDown : function(e){
60828
60829     },
60830
60831     callHandleMouseDown : function(e){
60832         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60833     }
60834 });
60835 /*
60836  * Based on:
60837  * Ext JS Library 1.1.1
60838  * Copyright(c) 2006-2007, Ext JS, LLC.
60839  *
60840  * Originally Released Under LGPL - original licence link has changed is not relivant.
60841  *
60842  * Fork - LGPL
60843  * <script type="text/javascript">
60844  */
60845  /**
60846  * @extends Roo.dd.DDProxy
60847  * @class Roo.grid.SplitDragZone
60848  * Support for Column Header resizing
60849  * @constructor
60850  * @param {Object} config
60851  */
60852 // private
60853 // This is a support class used internally by the Grid components
60854 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60855     this.grid = grid;
60856     this.view = grid.getView();
60857     this.proxy = this.view.resizeProxy;
60858     Roo.grid.SplitDragZone.superclass.constructor.call(
60859         this,
60860         hd, // ID
60861         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60862         {  // CONFIG
60863             dragElId : Roo.id(this.proxy.dom),
60864             resizeFrame:false
60865         }
60866     );
60867     
60868     this.setHandleElId(Roo.id(hd));
60869     if (hd2 !== false) {
60870         this.setOuterHandleElId(Roo.id(hd2));
60871     }
60872     
60873     this.scroll = false;
60874 };
60875 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60876     fly: Roo.Element.fly,
60877
60878     b4StartDrag : function(x, y){
60879         this.view.headersDisabled = true;
60880         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60881                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60882         );
60883         this.proxy.setHeight(h);
60884         
60885         // for old system colWidth really stored the actual width?
60886         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60887         // which in reality did not work.. - it worked only for fixed sizes
60888         // for resizable we need to use actual sizes.
60889         var w = this.cm.getColumnWidth(this.cellIndex);
60890         if (!this.view.mainWrap) {
60891             // bootstrap.
60892             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60893         }
60894         
60895         
60896         
60897         // this was w-this.grid.minColumnWidth;
60898         // doesnt really make sense? - w = thie curren width or the rendered one?
60899         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60900         this.resetConstraints();
60901         this.setXConstraint(minw, 1000);
60902         this.setYConstraint(0, 0);
60903         this.minX = x - minw;
60904         this.maxX = x + 1000;
60905         this.startPos = x;
60906         if (!this.view.mainWrap) { // this is Bootstrap code..
60907             this.getDragEl().style.display='block';
60908         }
60909         
60910         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60911     },
60912
60913
60914     handleMouseDown : function(e){
60915         ev = Roo.EventObject.setEvent(e);
60916         var t = this.fly(ev.getTarget());
60917         if(t.hasClass("x-grid-split")){
60918             this.cellIndex = this.view.getCellIndex(t.dom);
60919             this.split = t.dom;
60920             this.cm = this.grid.colModel;
60921             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60922                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60923             }
60924         }
60925     },
60926
60927     endDrag : function(e){
60928         this.view.headersDisabled = false;
60929         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60930         var diff = endX - this.startPos;
60931         // 
60932         var w = this.cm.getColumnWidth(this.cellIndex);
60933         if (!this.view.mainWrap) {
60934             w = 0;
60935         }
60936         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60937     },
60938
60939     autoOffset : function(){
60940         this.setDelta(0,0);
60941     }
60942 });/*
60943  * Based on:
60944  * Ext JS Library 1.1.1
60945  * Copyright(c) 2006-2007, Ext JS, LLC.
60946  *
60947  * Originally Released Under LGPL - original licence link has changed is not relivant.
60948  *
60949  * Fork - LGPL
60950  * <script type="text/javascript">
60951  */
60952  
60953 // private
60954 // This is a support class used internally by the Grid components
60955 Roo.grid.GridDragZone = function(grid, config){
60956     this.view = grid.getView();
60957     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60958     if(this.view.lockedBody){
60959         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60960         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60961     }
60962     this.scroll = false;
60963     this.grid = grid;
60964     this.ddel = document.createElement('div');
60965     this.ddel.className = 'x-grid-dd-wrap';
60966 };
60967
60968 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60969     ddGroup : "GridDD",
60970
60971     getDragData : function(e){
60972         var t = Roo.lib.Event.getTarget(e);
60973         var rowIndex = this.view.findRowIndex(t);
60974         var sm = this.grid.selModel;
60975             
60976         //Roo.log(rowIndex);
60977         
60978         if (sm.getSelectedCell) {
60979             // cell selection..
60980             if (!sm.getSelectedCell()) {
60981                 return false;
60982             }
60983             if (rowIndex != sm.getSelectedCell()[0]) {
60984                 return false;
60985             }
60986         
60987         }
60988         if (sm.getSelections && sm.getSelections().length < 1) {
60989             return false;
60990         }
60991         
60992         
60993         // before it used to all dragging of unseleted... - now we dont do that.
60994         if(rowIndex !== false){
60995             
60996             // if editorgrid.. 
60997             
60998             
60999             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
61000                
61001             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
61002               //  
61003             //}
61004             if (e.hasModifier()){
61005                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
61006             }
61007             
61008             Roo.log("getDragData");
61009             
61010             return {
61011                 grid: this.grid,
61012                 ddel: this.ddel,
61013                 rowIndex: rowIndex,
61014                 selections: sm.getSelections ? sm.getSelections() : (
61015                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61016             };
61017         }
61018         return false;
61019     },
61020     
61021     
61022     onInitDrag : function(e){
61023         var data = this.dragData;
61024         this.ddel.innerHTML = this.grid.getDragDropText();
61025         this.proxy.update(this.ddel);
61026         // fire start drag?
61027     },
61028
61029     afterRepair : function(){
61030         this.dragging = false;
61031     },
61032
61033     getRepairXY : function(e, data){
61034         return false;
61035     },
61036
61037     onEndDrag : function(data, e){
61038         // fire end drag?
61039     },
61040
61041     onValidDrop : function(dd, e, id){
61042         // fire drag drop?
61043         this.hideProxy();
61044     },
61045
61046     beforeInvalidDrop : function(e, id){
61047
61048     }
61049 });/*
61050  * Based on:
61051  * Ext JS Library 1.1.1
61052  * Copyright(c) 2006-2007, Ext JS, LLC.
61053  *
61054  * Originally Released Under LGPL - original licence link has changed is not relivant.
61055  *
61056  * Fork - LGPL
61057  * <script type="text/javascript">
61058  */
61059  
61060
61061 /**
61062  * @class Roo.grid.ColumnModel
61063  * @extends Roo.util.Observable
61064  * This is the default implementation of a ColumnModel used by the Grid. It defines
61065  * the columns in the grid.
61066  * <br>Usage:<br>
61067  <pre><code>
61068  var colModel = new Roo.grid.ColumnModel([
61069         {header: "Ticker", width: 60, sortable: true, locked: true},
61070         {header: "Company Name", width: 150, sortable: true},
61071         {header: "Market Cap.", width: 100, sortable: true},
61072         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61073         {header: "Employees", width: 100, sortable: true, resizable: false}
61074  ]);
61075  </code></pre>
61076  * <p>
61077  
61078  * The config options listed for this class are options which may appear in each
61079  * individual column definition.
61080  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61081  * @constructor
61082  * @param {Object} config An Array of column config objects. See this class's
61083  * config objects for details.
61084 */
61085 Roo.grid.ColumnModel = function(config){
61086         /**
61087      * The config passed into the constructor
61088      */
61089     this.config = []; //config;
61090     this.lookup = {};
61091
61092     // if no id, create one
61093     // if the column does not have a dataIndex mapping,
61094     // map it to the order it is in the config
61095     for(var i = 0, len = config.length; i < len; i++){
61096         this.addColumn(config[i]);
61097         
61098     }
61099
61100     /**
61101      * The width of columns which have no width specified (defaults to 100)
61102      * @type Number
61103      */
61104     this.defaultWidth = 100;
61105
61106     /**
61107      * Default sortable of columns which have no sortable specified (defaults to false)
61108      * @type Boolean
61109      */
61110     this.defaultSortable = false;
61111
61112     this.addEvents({
61113         /**
61114              * @event widthchange
61115              * Fires when the width of a column changes.
61116              * @param {ColumnModel} this
61117              * @param {Number} columnIndex The column index
61118              * @param {Number} newWidth The new width
61119              */
61120             "widthchange": true,
61121         /**
61122              * @event headerchange
61123              * Fires when the text of a header changes.
61124              * @param {ColumnModel} this
61125              * @param {Number} columnIndex The column index
61126              * @param {Number} newText The new header text
61127              */
61128             "headerchange": true,
61129         /**
61130              * @event hiddenchange
61131              * Fires when a column is hidden or "unhidden".
61132              * @param {ColumnModel} this
61133              * @param {Number} columnIndex The column index
61134              * @param {Boolean} hidden true if hidden, false otherwise
61135              */
61136             "hiddenchange": true,
61137             /**
61138          * @event columnmoved
61139          * Fires when a column is moved.
61140          * @param {ColumnModel} this
61141          * @param {Number} oldIndex
61142          * @param {Number} newIndex
61143          */
61144         "columnmoved" : true,
61145         /**
61146          * @event columlockchange
61147          * Fires when a column's locked state is changed
61148          * @param {ColumnModel} this
61149          * @param {Number} colIndex
61150          * @param {Boolean} locked true if locked
61151          */
61152         "columnlockchange" : true
61153     });
61154     Roo.grid.ColumnModel.superclass.constructor.call(this);
61155 };
61156 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61157     /**
61158      * @cfg {String} header The header text to display in the Grid view.
61159      */
61160         /**
61161      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61162      */
61163         /**
61164      * @cfg {String} smHeader Header at Bootsrap Small width
61165      */
61166         /**
61167      * @cfg {String} mdHeader Header at Bootsrap Medium width
61168      */
61169         /**
61170      * @cfg {String} lgHeader Header at Bootsrap Large width
61171      */
61172         /**
61173      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61174      */
61175     /**
61176      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61177      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61178      * specified, the column's index is used as an index into the Record's data Array.
61179      */
61180     /**
61181      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61182      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61183      */
61184     /**
61185      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61186      * Defaults to the value of the {@link #defaultSortable} property.
61187      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61188      */
61189     /**
61190      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61191      */
61192     /**
61193      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61194      */
61195     /**
61196      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61197      */
61198     /**
61199      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61200      */
61201     /**
61202      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61203      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61204      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61205      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61206      */
61207        /**
61208      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61209      */
61210     /**
61211      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61212      */
61213     /**
61214      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61215      */
61216     /**
61217      * @cfg {String} cursor (Optional)
61218      */
61219     /**
61220      * @cfg {String} tooltip (Optional)
61221      */
61222     /**
61223      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61224      */
61225     /**
61226      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61227      */
61228     /**
61229      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61230      */
61231     /**
61232      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61233      */
61234         /**
61235      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61236      */
61237     /**
61238      * Returns the id of the column at the specified index.
61239      * @param {Number} index The column index
61240      * @return {String} the id
61241      */
61242     getColumnId : function(index){
61243         return this.config[index].id;
61244     },
61245
61246     /**
61247      * Returns the column for a specified id.
61248      * @param {String} id The column id
61249      * @return {Object} the column
61250      */
61251     getColumnById : function(id){
61252         return this.lookup[id];
61253     },
61254
61255     
61256     /**
61257      * Returns the column Object for a specified dataIndex.
61258      * @param {String} dataIndex The column dataIndex
61259      * @return {Object|Boolean} the column or false if not found
61260      */
61261     getColumnByDataIndex: function(dataIndex){
61262         var index = this.findColumnIndex(dataIndex);
61263         return index > -1 ? this.config[index] : false;
61264     },
61265     
61266     /**
61267      * Returns the index for a specified column id.
61268      * @param {String} id The column id
61269      * @return {Number} the index, or -1 if not found
61270      */
61271     getIndexById : function(id){
61272         for(var i = 0, len = this.config.length; i < len; i++){
61273             if(this.config[i].id == id){
61274                 return i;
61275             }
61276         }
61277         return -1;
61278     },
61279     
61280     /**
61281      * Returns the index for a specified column dataIndex.
61282      * @param {String} dataIndex The column dataIndex
61283      * @return {Number} the index, or -1 if not found
61284      */
61285     
61286     findColumnIndex : function(dataIndex){
61287         for(var i = 0, len = this.config.length; i < len; i++){
61288             if(this.config[i].dataIndex == dataIndex){
61289                 return i;
61290             }
61291         }
61292         return -1;
61293     },
61294     
61295     
61296     moveColumn : function(oldIndex, newIndex){
61297         var c = this.config[oldIndex];
61298         this.config.splice(oldIndex, 1);
61299         this.config.splice(newIndex, 0, c);
61300         this.dataMap = null;
61301         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61302     },
61303
61304     isLocked : function(colIndex){
61305         return this.config[colIndex].locked === true;
61306     },
61307
61308     setLocked : function(colIndex, value, suppressEvent){
61309         if(this.isLocked(colIndex) == value){
61310             return;
61311         }
61312         this.config[colIndex].locked = value;
61313         if(!suppressEvent){
61314             this.fireEvent("columnlockchange", this, colIndex, value);
61315         }
61316     },
61317
61318     getTotalLockedWidth : function(){
61319         var totalWidth = 0;
61320         for(var i = 0; i < this.config.length; i++){
61321             if(this.isLocked(i) && !this.isHidden(i)){
61322                 this.totalWidth += this.getColumnWidth(i);
61323             }
61324         }
61325         return totalWidth;
61326     },
61327
61328     getLockedCount : function(){
61329         for(var i = 0, len = this.config.length; i < len; i++){
61330             if(!this.isLocked(i)){
61331                 return i;
61332             }
61333         }
61334         
61335         return this.config.length;
61336     },
61337
61338     /**
61339      * Returns the number of columns.
61340      * @return {Number}
61341      */
61342     getColumnCount : function(visibleOnly){
61343         if(visibleOnly === true){
61344             var c = 0;
61345             for(var i = 0, len = this.config.length; i < len; i++){
61346                 if(!this.isHidden(i)){
61347                     c++;
61348                 }
61349             }
61350             return c;
61351         }
61352         return this.config.length;
61353     },
61354
61355     /**
61356      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61357      * @param {Function} fn
61358      * @param {Object} scope (optional)
61359      * @return {Array} result
61360      */
61361     getColumnsBy : function(fn, scope){
61362         var r = [];
61363         for(var i = 0, len = this.config.length; i < len; i++){
61364             var c = this.config[i];
61365             if(fn.call(scope||this, c, i) === true){
61366                 r[r.length] = c;
61367             }
61368         }
61369         return r;
61370     },
61371
61372     /**
61373      * Returns true if the specified column is sortable.
61374      * @param {Number} col The column index
61375      * @return {Boolean}
61376      */
61377     isSortable : function(col){
61378         if(typeof this.config[col].sortable == "undefined"){
61379             return this.defaultSortable;
61380         }
61381         return this.config[col].sortable;
61382     },
61383
61384     /**
61385      * Returns the rendering (formatting) function defined for the column.
61386      * @param {Number} col The column index.
61387      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61388      */
61389     getRenderer : function(col){
61390         if(!this.config[col].renderer){
61391             return Roo.grid.ColumnModel.defaultRenderer;
61392         }
61393         return this.config[col].renderer;
61394     },
61395
61396     /**
61397      * Sets the rendering (formatting) function for a column.
61398      * @param {Number} col The column index
61399      * @param {Function} fn The function to use to process the cell's raw data
61400      * to return HTML markup for the grid view. The render function is called with
61401      * the following parameters:<ul>
61402      * <li>Data value.</li>
61403      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61404      * <li>css A CSS style string to apply to the table cell.</li>
61405      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61406      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61407      * <li>Row index</li>
61408      * <li>Column index</li>
61409      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61410      */
61411     setRenderer : function(col, fn){
61412         this.config[col].renderer = fn;
61413     },
61414
61415     /**
61416      * Returns the width for the specified column.
61417      * @param {Number} col The column index
61418      * @param (optional) {String} gridSize bootstrap width size.
61419      * @return {Number}
61420      */
61421     getColumnWidth : function(col, gridSize)
61422         {
61423                 var cfg = this.config[col];
61424                 
61425                 if (typeof(gridSize) == 'undefined') {
61426                         return cfg.width * 1 || this.defaultWidth;
61427                 }
61428                 if (gridSize === false) { // if we set it..
61429                         return cfg.width || false;
61430                 }
61431                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61432                 
61433                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61434                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61435                                 continue;
61436                         }
61437                         return cfg[ sizes[i] ];
61438                 }
61439                 return 1;
61440                 
61441     },
61442
61443     /**
61444      * Sets the width for a column.
61445      * @param {Number} col The column index
61446      * @param {Number} width The new width
61447      */
61448     setColumnWidth : function(col, width, suppressEvent){
61449         this.config[col].width = width;
61450         this.totalWidth = null;
61451         if(!suppressEvent){
61452              this.fireEvent("widthchange", this, col, width);
61453         }
61454     },
61455
61456     /**
61457      * Returns the total width of all columns.
61458      * @param {Boolean} includeHidden True to include hidden column widths
61459      * @return {Number}
61460      */
61461     getTotalWidth : function(includeHidden){
61462         if(!this.totalWidth){
61463             this.totalWidth = 0;
61464             for(var i = 0, len = this.config.length; i < len; i++){
61465                 if(includeHidden || !this.isHidden(i)){
61466                     this.totalWidth += this.getColumnWidth(i);
61467                 }
61468             }
61469         }
61470         return this.totalWidth;
61471     },
61472
61473     /**
61474      * Returns the header for the specified column.
61475      * @param {Number} col The column index
61476      * @return {String}
61477      */
61478     getColumnHeader : function(col){
61479         return this.config[col].header;
61480     },
61481
61482     /**
61483      * Sets the header for a column.
61484      * @param {Number} col The column index
61485      * @param {String} header The new header
61486      */
61487     setColumnHeader : function(col, header){
61488         this.config[col].header = header;
61489         this.fireEvent("headerchange", this, col, header);
61490     },
61491
61492     /**
61493      * Returns the tooltip for the specified column.
61494      * @param {Number} col The column index
61495      * @return {String}
61496      */
61497     getColumnTooltip : function(col){
61498             return this.config[col].tooltip;
61499     },
61500     /**
61501      * Sets the tooltip for a column.
61502      * @param {Number} col The column index
61503      * @param {String} tooltip The new tooltip
61504      */
61505     setColumnTooltip : function(col, tooltip){
61506             this.config[col].tooltip = tooltip;
61507     },
61508
61509     /**
61510      * Returns the dataIndex for the specified column.
61511      * @param {Number} col The column index
61512      * @return {Number}
61513      */
61514     getDataIndex : function(col){
61515         return this.config[col].dataIndex;
61516     },
61517
61518     /**
61519      * Sets the dataIndex for a column.
61520      * @param {Number} col The column index
61521      * @param {Number} dataIndex The new dataIndex
61522      */
61523     setDataIndex : function(col, dataIndex){
61524         this.config[col].dataIndex = dataIndex;
61525     },
61526
61527     
61528     
61529     /**
61530      * Returns true if the cell is editable.
61531      * @param {Number} colIndex The column index
61532      * @param {Number} rowIndex The row index - this is nto actually used..?
61533      * @return {Boolean}
61534      */
61535     isCellEditable : function(colIndex, rowIndex){
61536         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61537     },
61538
61539     /**
61540      * Returns the editor defined for the cell/column.
61541      * return false or null to disable editing.
61542      * @param {Number} colIndex The column index
61543      * @param {Number} rowIndex The row index
61544      * @return {Object}
61545      */
61546     getCellEditor : function(colIndex, rowIndex){
61547         return this.config[colIndex].editor;
61548     },
61549
61550     /**
61551      * Sets if a column is editable.
61552      * @param {Number} col The column index
61553      * @param {Boolean} editable True if the column is editable
61554      */
61555     setEditable : function(col, editable){
61556         this.config[col].editable = editable;
61557     },
61558
61559
61560     /**
61561      * Returns true if the column is hidden.
61562      * @param {Number} colIndex The column index
61563      * @return {Boolean}
61564      */
61565     isHidden : function(colIndex){
61566         return this.config[colIndex].hidden;
61567     },
61568
61569
61570     /**
61571      * Returns true if the column width cannot be changed
61572      */
61573     isFixed : function(colIndex){
61574         return this.config[colIndex].fixed;
61575     },
61576
61577     /**
61578      * Returns true if the column can be resized
61579      * @return {Boolean}
61580      */
61581     isResizable : function(colIndex){
61582         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61583     },
61584     /**
61585      * Sets if a column is hidden.
61586      * @param {Number} colIndex The column index
61587      * @param {Boolean} hidden True if the column is hidden
61588      */
61589     setHidden : function(colIndex, hidden){
61590         this.config[colIndex].hidden = hidden;
61591         this.totalWidth = null;
61592         this.fireEvent("hiddenchange", this, colIndex, hidden);
61593     },
61594
61595     /**
61596      * Sets the editor for a column.
61597      * @param {Number} col The column index
61598      * @param {Object} editor The editor object
61599      */
61600     setEditor : function(col, editor){
61601         this.config[col].editor = editor;
61602     },
61603     /**
61604      * Add a column (experimental...) - defaults to adding to the end..
61605      * @param {Object} config 
61606     */
61607     addColumn : function(c)
61608     {
61609     
61610         var i = this.config.length;
61611         this.config[i] = c;
61612         
61613         if(typeof c.dataIndex == "undefined"){
61614             c.dataIndex = i;
61615         }
61616         if(typeof c.renderer == "string"){
61617             c.renderer = Roo.util.Format[c.renderer];
61618         }
61619         if(typeof c.id == "undefined"){
61620             c.id = Roo.id();
61621         }
61622         if(c.editor && c.editor.xtype){
61623             c.editor  = Roo.factory(c.editor, Roo.grid);
61624         }
61625         if(c.editor && c.editor.isFormField){
61626             c.editor = new Roo.grid.GridEditor(c.editor);
61627         }
61628         this.lookup[c.id] = c;
61629     }
61630     
61631 });
61632
61633 Roo.grid.ColumnModel.defaultRenderer = function(value)
61634 {
61635     if(typeof value == "object") {
61636         return value;
61637     }
61638         if(typeof value == "string" && value.length < 1){
61639             return "&#160;";
61640         }
61641     
61642         return String.format("{0}", value);
61643 };
61644
61645 // Alias for backwards compatibility
61646 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61647 /*
61648  * Based on:
61649  * Ext JS Library 1.1.1
61650  * Copyright(c) 2006-2007, Ext JS, LLC.
61651  *
61652  * Originally Released Under LGPL - original licence link has changed is not relivant.
61653  *
61654  * Fork - LGPL
61655  * <script type="text/javascript">
61656  */
61657
61658 /**
61659  * @class Roo.grid.AbstractSelectionModel
61660  * @extends Roo.util.Observable
61661  * @abstract
61662  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61663  * implemented by descendant classes.  This class should not be directly instantiated.
61664  * @constructor
61665  */
61666 Roo.grid.AbstractSelectionModel = function(){
61667     this.locked = false;
61668     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61669 };
61670
61671 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61672     /** @ignore Called by the grid automatically. Do not call directly. */
61673     init : function(grid){
61674         this.grid = grid;
61675         this.initEvents();
61676     },
61677
61678     /**
61679      * Locks the selections.
61680      */
61681     lock : function(){
61682         this.locked = true;
61683     },
61684
61685     /**
61686      * Unlocks the selections.
61687      */
61688     unlock : function(){
61689         this.locked = false;
61690     },
61691
61692     /**
61693      * Returns true if the selections are locked.
61694      * @return {Boolean}
61695      */
61696     isLocked : function(){
61697         return this.locked;
61698     }
61699 });/*
61700  * Based on:
61701  * Ext JS Library 1.1.1
61702  * Copyright(c) 2006-2007, Ext JS, LLC.
61703  *
61704  * Originally Released Under LGPL - original licence link has changed is not relivant.
61705  *
61706  * Fork - LGPL
61707  * <script type="text/javascript">
61708  */
61709 /**
61710  * @extends Roo.grid.AbstractSelectionModel
61711  * @class Roo.grid.RowSelectionModel
61712  * The default SelectionModel used by {@link Roo.grid.Grid}.
61713  * It supports multiple selections and keyboard selection/navigation. 
61714  * @constructor
61715  * @param {Object} config
61716  */
61717 Roo.grid.RowSelectionModel = function(config){
61718     Roo.apply(this, config);
61719     this.selections = new Roo.util.MixedCollection(false, function(o){
61720         return o.id;
61721     });
61722
61723     this.last = false;
61724     this.lastActive = false;
61725
61726     this.addEvents({
61727         /**
61728         * @event selectionchange
61729         * Fires when the selection changes
61730         * @param {SelectionModel} this
61731         */
61732        "selectionchange" : true,
61733        /**
61734         * @event afterselectionchange
61735         * Fires after the selection changes (eg. by key press or clicking)
61736         * @param {SelectionModel} this
61737         */
61738        "afterselectionchange" : true,
61739        /**
61740         * @event beforerowselect
61741         * Fires when a row is selected being selected, return false to cancel.
61742         * @param {SelectionModel} this
61743         * @param {Number} rowIndex The selected index
61744         * @param {Boolean} keepExisting False if other selections will be cleared
61745         */
61746        "beforerowselect" : true,
61747        /**
61748         * @event rowselect
61749         * Fires when a row is selected.
61750         * @param {SelectionModel} this
61751         * @param {Number} rowIndex The selected index
61752         * @param {Roo.data.Record} r The record
61753         */
61754        "rowselect" : true,
61755        /**
61756         * @event rowdeselect
61757         * Fires when a row is deselected.
61758         * @param {SelectionModel} this
61759         * @param {Number} rowIndex The selected index
61760         */
61761         "rowdeselect" : true
61762     });
61763     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61764     this.locked = false;
61765 };
61766
61767 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61768     /**
61769      * @cfg {Boolean} singleSelect
61770      * True to allow selection of only one row at a time (defaults to false)
61771      */
61772     singleSelect : false,
61773
61774     // private
61775     initEvents : function(){
61776
61777         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61778             this.grid.on("mousedown", this.handleMouseDown, this);
61779         }else{ // allow click to work like normal
61780             this.grid.on("rowclick", this.handleDragableRowClick, this);
61781         }
61782         // bootstrap does not have a view..
61783         var view = this.grid.view ? this.grid.view : this.grid;
61784         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61785             "up" : function(e){
61786                 if(!e.shiftKey){
61787                     this.selectPrevious(e.shiftKey);
61788                 }else if(this.last !== false && this.lastActive !== false){
61789                     var last = this.last;
61790                     this.selectRange(this.last,  this.lastActive-1);
61791                     view.focusRow(this.lastActive);
61792                     if(last !== false){
61793                         this.last = last;
61794                     }
61795                 }else{
61796                     this.selectFirstRow();
61797                 }
61798                 this.fireEvent("afterselectionchange", this);
61799             },
61800             "down" : function(e){
61801                 if(!e.shiftKey){
61802                     this.selectNext(e.shiftKey);
61803                 }else if(this.last !== false && this.lastActive !== false){
61804                     var last = this.last;
61805                     this.selectRange(this.last,  this.lastActive+1);
61806                     view.focusRow(this.lastActive);
61807                     if(last !== false){
61808                         this.last = last;
61809                     }
61810                 }else{
61811                     this.selectFirstRow();
61812                 }
61813                 this.fireEvent("afterselectionchange", this);
61814             },
61815             scope: this
61816         });
61817
61818          
61819         view.on("refresh", this.onRefresh, this);
61820         view.on("rowupdated", this.onRowUpdated, this);
61821         view.on("rowremoved", this.onRemove, this);
61822     },
61823
61824     // private
61825     onRefresh : function(){
61826         var ds = this.grid.ds, i, v = this.grid.view;
61827         var s = this.selections;
61828         s.each(function(r){
61829             if((i = ds.indexOfId(r.id)) != -1){
61830                 v.onRowSelect(i);
61831                 s.add(ds.getAt(i)); // updating the selection relate data
61832             }else{
61833                 s.remove(r);
61834             }
61835         });
61836     },
61837
61838     // private
61839     onRemove : function(v, index, r){
61840         this.selections.remove(r);
61841     },
61842
61843     // private
61844     onRowUpdated : function(v, index, r){
61845         if(this.isSelected(r)){
61846             v.onRowSelect(index);
61847         }
61848     },
61849
61850     /**
61851      * Select records.
61852      * @param {Array} records The records to select
61853      * @param {Boolean} keepExisting (optional) True to keep existing selections
61854      */
61855     selectRecords : function(records, keepExisting){
61856         if(!keepExisting){
61857             this.clearSelections();
61858         }
61859         var ds = this.grid.ds;
61860         for(var i = 0, len = records.length; i < len; i++){
61861             this.selectRow(ds.indexOf(records[i]), true);
61862         }
61863     },
61864
61865     /**
61866      * Gets the number of selected rows.
61867      * @return {Number}
61868      */
61869     getCount : function(){
61870         return this.selections.length;
61871     },
61872
61873     /**
61874      * Selects the first row in the grid.
61875      */
61876     selectFirstRow : function(){
61877         this.selectRow(0);
61878     },
61879
61880     /**
61881      * Select the last row.
61882      * @param {Boolean} keepExisting (optional) True to keep existing selections
61883      */
61884     selectLastRow : function(keepExisting){
61885         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61886     },
61887
61888     /**
61889      * Selects the row immediately following the last selected row.
61890      * @param {Boolean} keepExisting (optional) True to keep existing selections
61891      */
61892     selectNext : function(keepExisting){
61893         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61894             this.selectRow(this.last+1, keepExisting);
61895             var view = this.grid.view ? this.grid.view : this.grid;
61896             view.focusRow(this.last);
61897         }
61898     },
61899
61900     /**
61901      * Selects the row that precedes the last selected row.
61902      * @param {Boolean} keepExisting (optional) True to keep existing selections
61903      */
61904     selectPrevious : function(keepExisting){
61905         if(this.last){
61906             this.selectRow(this.last-1, keepExisting);
61907             var view = this.grid.view ? this.grid.view : this.grid;
61908             view.focusRow(this.last);
61909         }
61910     },
61911
61912     /**
61913      * Returns the selected records
61914      * @return {Array} Array of selected records
61915      */
61916     getSelections : function(){
61917         return [].concat(this.selections.items);
61918     },
61919
61920     /**
61921      * Returns the first selected record.
61922      * @return {Record}
61923      */
61924     getSelected : function(){
61925         return this.selections.itemAt(0);
61926     },
61927
61928
61929     /**
61930      * Clears all selections.
61931      */
61932     clearSelections : function(fast){
61933         if(this.locked) {
61934             return;
61935         }
61936         if(fast !== true){
61937             var ds = this.grid.ds;
61938             var s = this.selections;
61939             s.each(function(r){
61940                 this.deselectRow(ds.indexOfId(r.id));
61941             }, this);
61942             s.clear();
61943         }else{
61944             this.selections.clear();
61945         }
61946         this.last = false;
61947     },
61948
61949
61950     /**
61951      * Selects all rows.
61952      */
61953     selectAll : function(){
61954         if(this.locked) {
61955             return;
61956         }
61957         this.selections.clear();
61958         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61959             this.selectRow(i, true);
61960         }
61961     },
61962
61963     /**
61964      * Returns True if there is a selection.
61965      * @return {Boolean}
61966      */
61967     hasSelection : function(){
61968         return this.selections.length > 0;
61969     },
61970
61971     /**
61972      * Returns True if the specified row is selected.
61973      * @param {Number/Record} record The record or index of the record to check
61974      * @return {Boolean}
61975      */
61976     isSelected : function(index){
61977         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61978         return (r && this.selections.key(r.id) ? true : false);
61979     },
61980
61981     /**
61982      * Returns True if the specified record id is selected.
61983      * @param {String} id The id of record to check
61984      * @return {Boolean}
61985      */
61986     isIdSelected : function(id){
61987         return (this.selections.key(id) ? true : false);
61988     },
61989
61990     // private
61991     handleMouseDown : function(e, t)
61992     {
61993         var view = this.grid.view ? this.grid.view : this.grid;
61994         var rowIndex;
61995         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61996             return;
61997         };
61998         if(e.shiftKey && this.last !== false){
61999             var last = this.last;
62000             this.selectRange(last, rowIndex, e.ctrlKey);
62001             this.last = last; // reset the last
62002             view.focusRow(rowIndex);
62003         }else{
62004             var isSelected = this.isSelected(rowIndex);
62005             if(e.button !== 0 && isSelected){
62006                 view.focusRow(rowIndex);
62007             }else if(e.ctrlKey && isSelected){
62008                 this.deselectRow(rowIndex);
62009             }else if(!isSelected){
62010                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
62011                 view.focusRow(rowIndex);
62012             }
62013         }
62014         this.fireEvent("afterselectionchange", this);
62015     },
62016     // private
62017     handleDragableRowClick :  function(grid, rowIndex, e) 
62018     {
62019         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62020             this.selectRow(rowIndex, false);
62021             var view = this.grid.view ? this.grid.view : this.grid;
62022             view.focusRow(rowIndex);
62023              this.fireEvent("afterselectionchange", this);
62024         }
62025     },
62026     
62027     /**
62028      * Selects multiple rows.
62029      * @param {Array} rows Array of the indexes of the row to select
62030      * @param {Boolean} keepExisting (optional) True to keep existing selections
62031      */
62032     selectRows : function(rows, keepExisting){
62033         if(!keepExisting){
62034             this.clearSelections();
62035         }
62036         for(var i = 0, len = rows.length; i < len; i++){
62037             this.selectRow(rows[i], true);
62038         }
62039     },
62040
62041     /**
62042      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62043      * @param {Number} startRow The index of the first row in the range
62044      * @param {Number} endRow The index of the last row in the range
62045      * @param {Boolean} keepExisting (optional) True to retain existing selections
62046      */
62047     selectRange : function(startRow, endRow, keepExisting){
62048         if(this.locked) {
62049             return;
62050         }
62051         if(!keepExisting){
62052             this.clearSelections();
62053         }
62054         if(startRow <= endRow){
62055             for(var i = startRow; i <= endRow; i++){
62056                 this.selectRow(i, true);
62057             }
62058         }else{
62059             for(var i = startRow; i >= endRow; i--){
62060                 this.selectRow(i, true);
62061             }
62062         }
62063     },
62064
62065     /**
62066      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62067      * @param {Number} startRow The index of the first row in the range
62068      * @param {Number} endRow The index of the last row in the range
62069      */
62070     deselectRange : function(startRow, endRow, preventViewNotify){
62071         if(this.locked) {
62072             return;
62073         }
62074         for(var i = startRow; i <= endRow; i++){
62075             this.deselectRow(i, preventViewNotify);
62076         }
62077     },
62078
62079     /**
62080      * Selects a row.
62081      * @param {Number} row The index of the row to select
62082      * @param {Boolean} keepExisting (optional) True to keep existing selections
62083      */
62084     selectRow : function(index, keepExisting, preventViewNotify){
62085         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62086             return;
62087         }
62088         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62089             if(!keepExisting || this.singleSelect){
62090                 this.clearSelections();
62091             }
62092             var r = this.grid.ds.getAt(index);
62093             this.selections.add(r);
62094             this.last = this.lastActive = index;
62095             if(!preventViewNotify){
62096                 var view = this.grid.view ? this.grid.view : this.grid;
62097                 view.onRowSelect(index);
62098             }
62099             this.fireEvent("rowselect", this, index, r);
62100             this.fireEvent("selectionchange", this);
62101         }
62102     },
62103
62104     /**
62105      * Deselects a row.
62106      * @param {Number} row The index of the row to deselect
62107      */
62108     deselectRow : function(index, preventViewNotify){
62109         if(this.locked) {
62110             return;
62111         }
62112         if(this.last == index){
62113             this.last = false;
62114         }
62115         if(this.lastActive == index){
62116             this.lastActive = false;
62117         }
62118         var r = this.grid.ds.getAt(index);
62119         this.selections.remove(r);
62120         if(!preventViewNotify){
62121             var view = this.grid.view ? this.grid.view : this.grid;
62122             view.onRowDeselect(index);
62123         }
62124         this.fireEvent("rowdeselect", this, index);
62125         this.fireEvent("selectionchange", this);
62126     },
62127
62128     // private
62129     restoreLast : function(){
62130         if(this._last){
62131             this.last = this._last;
62132         }
62133     },
62134
62135     // private
62136     acceptsNav : function(row, col, cm){
62137         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62138     },
62139
62140     // private
62141     onEditorKey : function(field, e){
62142         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62143         if(k == e.TAB){
62144             e.stopEvent();
62145             ed.completeEdit();
62146             if(e.shiftKey){
62147                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62148             }else{
62149                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62150             }
62151         }else if(k == e.ENTER && !e.ctrlKey){
62152             e.stopEvent();
62153             ed.completeEdit();
62154             if(e.shiftKey){
62155                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62156             }else{
62157                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62158             }
62159         }else if(k == e.ESC){
62160             ed.cancelEdit();
62161         }
62162         if(newCell){
62163             g.startEditing(newCell[0], newCell[1]);
62164         }
62165     }
62166 });/*
62167  * Based on:
62168  * Ext JS Library 1.1.1
62169  * Copyright(c) 2006-2007, Ext JS, LLC.
62170  *
62171  * Originally Released Under LGPL - original licence link has changed is not relivant.
62172  *
62173  * Fork - LGPL
62174  * <script type="text/javascript">
62175  */
62176 /**
62177  * @class Roo.grid.CellSelectionModel
62178  * @extends Roo.grid.AbstractSelectionModel
62179  * This class provides the basic implementation for cell selection in a grid.
62180  * @constructor
62181  * @param {Object} config The object containing the configuration of this model.
62182  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62183  */
62184 Roo.grid.CellSelectionModel = function(config){
62185     Roo.apply(this, config);
62186
62187     this.selection = null;
62188
62189     this.addEvents({
62190         /**
62191              * @event beforerowselect
62192              * Fires before a cell is selected.
62193              * @param {SelectionModel} this
62194              * @param {Number} rowIndex The selected row index
62195              * @param {Number} colIndex The selected cell index
62196              */
62197             "beforecellselect" : true,
62198         /**
62199              * @event cellselect
62200              * Fires when a cell is selected.
62201              * @param {SelectionModel} this
62202              * @param {Number} rowIndex The selected row index
62203              * @param {Number} colIndex The selected cell index
62204              */
62205             "cellselect" : true,
62206         /**
62207              * @event selectionchange
62208              * Fires when the active selection changes.
62209              * @param {SelectionModel} this
62210              * @param {Object} selection null for no selection or an object (o) with two properties
62211                 <ul>
62212                 <li>o.record: the record object for the row the selection is in</li>
62213                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62214                 </ul>
62215              */
62216             "selectionchange" : true,
62217         /**
62218              * @event tabend
62219              * Fires when the tab (or enter) was pressed on the last editable cell
62220              * You can use this to trigger add new row.
62221              * @param {SelectionModel} this
62222              */
62223             "tabend" : true,
62224          /**
62225              * @event beforeeditnext
62226              * Fires before the next editable sell is made active
62227              * You can use this to skip to another cell or fire the tabend
62228              *    if you set cell to false
62229              * @param {Object} eventdata object : { cell : [ row, col ] } 
62230              */
62231             "beforeeditnext" : true
62232     });
62233     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62234 };
62235
62236 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62237     
62238     enter_is_tab: false,
62239
62240     /** @ignore */
62241     initEvents : function(){
62242         this.grid.on("mousedown", this.handleMouseDown, this);
62243         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62244         var view = this.grid.view;
62245         view.on("refresh", this.onViewChange, this);
62246         view.on("rowupdated", this.onRowUpdated, this);
62247         view.on("beforerowremoved", this.clearSelections, this);
62248         view.on("beforerowsinserted", this.clearSelections, this);
62249         if(this.grid.isEditor){
62250             this.grid.on("beforeedit", this.beforeEdit,  this);
62251         }
62252     },
62253
62254         //private
62255     beforeEdit : function(e){
62256         this.select(e.row, e.column, false, true, e.record);
62257     },
62258
62259         //private
62260     onRowUpdated : function(v, index, r){
62261         if(this.selection && this.selection.record == r){
62262             v.onCellSelect(index, this.selection.cell[1]);
62263         }
62264     },
62265
62266         //private
62267     onViewChange : function(){
62268         this.clearSelections(true);
62269     },
62270
62271         /**
62272          * Returns the currently selected cell,.
62273          * @return {Array} The selected cell (row, column) or null if none selected.
62274          */
62275     getSelectedCell : function(){
62276         return this.selection ? this.selection.cell : null;
62277     },
62278
62279     /**
62280      * Clears all selections.
62281      * @param {Boolean} true to prevent the gridview from being notified about the change.
62282      */
62283     clearSelections : function(preventNotify){
62284         var s = this.selection;
62285         if(s){
62286             if(preventNotify !== true){
62287                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62288             }
62289             this.selection = null;
62290             this.fireEvent("selectionchange", this, null);
62291         }
62292     },
62293
62294     /**
62295      * Returns true if there is a selection.
62296      * @return {Boolean}
62297      */
62298     hasSelection : function(){
62299         return this.selection ? true : false;
62300     },
62301
62302     /** @ignore */
62303     handleMouseDown : function(e, t){
62304         var v = this.grid.getView();
62305         if(this.isLocked()){
62306             return;
62307         };
62308         var row = v.findRowIndex(t);
62309         var cell = v.findCellIndex(t);
62310         if(row !== false && cell !== false){
62311             this.select(row, cell);
62312         }
62313     },
62314
62315     /**
62316      * Selects a cell.
62317      * @param {Number} rowIndex
62318      * @param {Number} collIndex
62319      */
62320     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62321         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62322             this.clearSelections();
62323             r = r || this.grid.dataSource.getAt(rowIndex);
62324             this.selection = {
62325                 record : r,
62326                 cell : [rowIndex, colIndex]
62327             };
62328             if(!preventViewNotify){
62329                 var v = this.grid.getView();
62330                 v.onCellSelect(rowIndex, colIndex);
62331                 if(preventFocus !== true){
62332                     v.focusCell(rowIndex, colIndex);
62333                 }
62334             }
62335             this.fireEvent("cellselect", this, rowIndex, colIndex);
62336             this.fireEvent("selectionchange", this, this.selection);
62337         }
62338     },
62339
62340         //private
62341     isSelectable : function(rowIndex, colIndex, cm){
62342         return !cm.isHidden(colIndex);
62343     },
62344
62345     /** @ignore */
62346     handleKeyDown : function(e){
62347         //Roo.log('Cell Sel Model handleKeyDown');
62348         if(!e.isNavKeyPress()){
62349             return;
62350         }
62351         var g = this.grid, s = this.selection;
62352         if(!s){
62353             e.stopEvent();
62354             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62355             if(cell){
62356                 this.select(cell[0], cell[1]);
62357             }
62358             return;
62359         }
62360         var sm = this;
62361         var walk = function(row, col, step){
62362             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62363         };
62364         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62365         var newCell;
62366
62367       
62368
62369         switch(k){
62370             case e.TAB:
62371                 // handled by onEditorKey
62372                 if (g.isEditor && g.editing) {
62373                     return;
62374                 }
62375                 if(e.shiftKey) {
62376                     newCell = walk(r, c-1, -1);
62377                 } else {
62378                     newCell = walk(r, c+1, 1);
62379                 }
62380                 break;
62381             
62382             case e.DOWN:
62383                newCell = walk(r+1, c, 1);
62384                 break;
62385             
62386             case e.UP:
62387                 newCell = walk(r-1, c, -1);
62388                 break;
62389             
62390             case e.RIGHT:
62391                 newCell = walk(r, c+1, 1);
62392                 break;
62393             
62394             case e.LEFT:
62395                 newCell = walk(r, c-1, -1);
62396                 break;
62397             
62398             case e.ENTER:
62399                 
62400                 if(g.isEditor && !g.editing){
62401                    g.startEditing(r, c);
62402                    e.stopEvent();
62403                    return;
62404                 }
62405                 
62406                 
62407              break;
62408         };
62409         if(newCell){
62410             this.select(newCell[0], newCell[1]);
62411             e.stopEvent();
62412             
62413         }
62414     },
62415
62416     acceptsNav : function(row, col, cm){
62417         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62418     },
62419     /**
62420      * Selects a cell.
62421      * @param {Number} field (not used) - as it's normally used as a listener
62422      * @param {Number} e - event - fake it by using
62423      *
62424      * var e = Roo.EventObjectImpl.prototype;
62425      * e.keyCode = e.TAB
62426      *
62427      * 
62428      */
62429     onEditorKey : function(field, e){
62430         
62431         var k = e.getKey(),
62432             newCell,
62433             g = this.grid,
62434             ed = g.activeEditor,
62435             forward = false;
62436         ///Roo.log('onEditorKey' + k);
62437         
62438         
62439         if (this.enter_is_tab && k == e.ENTER) {
62440             k = e.TAB;
62441         }
62442         
62443         if(k == e.TAB){
62444             if(e.shiftKey){
62445                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62446             }else{
62447                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62448                 forward = true;
62449             }
62450             
62451             e.stopEvent();
62452             
62453         } else if(k == e.ENTER &&  !e.ctrlKey){
62454             ed.completeEdit();
62455             e.stopEvent();
62456             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62457         
62458                 } else if(k == e.ESC){
62459             ed.cancelEdit();
62460         }
62461                 
62462         if (newCell) {
62463             var ecall = { cell : newCell, forward : forward };
62464             this.fireEvent('beforeeditnext', ecall );
62465             newCell = ecall.cell;
62466                         forward = ecall.forward;
62467         }
62468                 
62469         if(newCell){
62470             //Roo.log('next cell after edit');
62471             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62472         } else if (forward) {
62473             // tabbed past last
62474             this.fireEvent.defer(100, this, ['tabend',this]);
62475         }
62476     }
62477 });/*
62478  * Based on:
62479  * Ext JS Library 1.1.1
62480  * Copyright(c) 2006-2007, Ext JS, LLC.
62481  *
62482  * Originally Released Under LGPL - original licence link has changed is not relivant.
62483  *
62484  * Fork - LGPL
62485  * <script type="text/javascript">
62486  */
62487  
62488 /**
62489  * @class Roo.grid.EditorGrid
62490  * @extends Roo.grid.Grid
62491  * Class for creating and editable grid.
62492  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62493  * The container MUST have some type of size defined for the grid to fill. The container will be 
62494  * automatically set to position relative if it isn't already.
62495  * @param {Object} dataSource The data model to bind to
62496  * @param {Object} colModel The column model with info about this grid's columns
62497  */
62498 Roo.grid.EditorGrid = function(container, config){
62499     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62500     this.getGridEl().addClass("xedit-grid");
62501
62502     if(!this.selModel){
62503         this.selModel = new Roo.grid.CellSelectionModel();
62504     }
62505
62506     this.activeEditor = null;
62507
62508         this.addEvents({
62509             /**
62510              * @event beforeedit
62511              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62512              * <ul style="padding:5px;padding-left:16px;">
62513              * <li>grid - This grid</li>
62514              * <li>record - The record being edited</li>
62515              * <li>field - The field name being edited</li>
62516              * <li>value - The value for the field being edited.</li>
62517              * <li>row - The grid row index</li>
62518              * <li>column - The grid column index</li>
62519              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62520              * </ul>
62521              * @param {Object} e An edit event (see above for description)
62522              */
62523             "beforeedit" : true,
62524             /**
62525              * @event afteredit
62526              * Fires after a cell is edited. <br />
62527              * <ul style="padding:5px;padding-left:16px;">
62528              * <li>grid - This grid</li>
62529              * <li>record - The record being edited</li>
62530              * <li>field - The field name being edited</li>
62531              * <li>value - The value being set</li>
62532              * <li>originalValue - The original value for the field, before the edit.</li>
62533              * <li>row - The grid row index</li>
62534              * <li>column - The grid column index</li>
62535              * </ul>
62536              * @param {Object} e An edit event (see above for description)
62537              */
62538             "afteredit" : true,
62539             /**
62540              * @event validateedit
62541              * Fires after a cell is edited, but before the value is set in the record. 
62542          * You can use this to modify the value being set in the field, Return false
62543              * to cancel the change. The edit event object has the following properties <br />
62544              * <ul style="padding:5px;padding-left:16px;">
62545          * <li>editor - This editor</li>
62546              * <li>grid - This grid</li>
62547              * <li>record - The record being edited</li>
62548              * <li>field - The field name being edited</li>
62549              * <li>value - The value being set</li>
62550              * <li>originalValue - The original value for the field, before the edit.</li>
62551              * <li>row - The grid row index</li>
62552              * <li>column - The grid column index</li>
62553              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62554              * </ul>
62555              * @param {Object} e An edit event (see above for description)
62556              */
62557             "validateedit" : true
62558         });
62559     this.on("bodyscroll", this.stopEditing,  this);
62560     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62561 };
62562
62563 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62564     /**
62565      * @cfg {Number} clicksToEdit
62566      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62567      */
62568     clicksToEdit: 2,
62569
62570     // private
62571     isEditor : true,
62572     // private
62573     trackMouseOver: false, // causes very odd FF errors
62574
62575     onCellDblClick : function(g, row, col){
62576         this.startEditing(row, col);
62577     },
62578
62579     onEditComplete : function(ed, value, startValue){
62580         this.editing = false;
62581         this.activeEditor = null;
62582         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62583         var r = ed.record;
62584         var field = this.colModel.getDataIndex(ed.col);
62585         var e = {
62586             grid: this,
62587             record: r,
62588             field: field,
62589             originalValue: startValue,
62590             value: value,
62591             row: ed.row,
62592             column: ed.col,
62593             cancel:false,
62594             editor: ed
62595         };
62596         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62597         cell.show();
62598           
62599         if(String(value) !== String(startValue)){
62600             
62601             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62602                 r.set(field, e.value);
62603                 // if we are dealing with a combo box..
62604                 // then we also set the 'name' colum to be the displayField
62605                 if (ed.field.displayField && ed.field.name) {
62606                     r.set(ed.field.name, ed.field.el.dom.value);
62607                 }
62608                 
62609                 delete e.cancel; //?? why!!!
62610                 this.fireEvent("afteredit", e);
62611             }
62612         } else {
62613             this.fireEvent("afteredit", e); // always fire it!
62614         }
62615         this.view.focusCell(ed.row, ed.col);
62616     },
62617
62618     /**
62619      * Starts editing the specified for the specified row/column
62620      * @param {Number} rowIndex
62621      * @param {Number} colIndex
62622      */
62623     startEditing : function(row, col){
62624         this.stopEditing();
62625         if(this.colModel.isCellEditable(col, row)){
62626             this.view.ensureVisible(row, col, true);
62627           
62628             var r = this.dataSource.getAt(row);
62629             var field = this.colModel.getDataIndex(col);
62630             var cell = Roo.get(this.view.getCell(row,col));
62631             var e = {
62632                 grid: this,
62633                 record: r,
62634                 field: field,
62635                 value: r.data[field],
62636                 row: row,
62637                 column: col,
62638                 cancel:false 
62639             };
62640             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62641                 this.editing = true;
62642                 var ed = this.colModel.getCellEditor(col, row);
62643                 
62644                 if (!ed) {
62645                     return;
62646                 }
62647                 if(!ed.rendered){
62648                     ed.render(ed.parentEl || document.body);
62649                 }
62650                 ed.field.reset();
62651                
62652                 cell.hide();
62653                 
62654                 (function(){ // complex but required for focus issues in safari, ie and opera
62655                     ed.row = row;
62656                     ed.col = col;
62657                     ed.record = r;
62658                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62659                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62660                     this.activeEditor = ed;
62661                     var v = r.data[field];
62662                     ed.startEdit(this.view.getCell(row, col), v);
62663                     // combo's with 'displayField and name set
62664                     if (ed.field.displayField && ed.field.name) {
62665                         ed.field.el.dom.value = r.data[ed.field.name];
62666                     }
62667                     
62668                     
62669                 }).defer(50, this);
62670             }
62671         }
62672     },
62673         
62674     /**
62675      * Stops any active editing
62676      */
62677     stopEditing : function(){
62678         if(this.activeEditor){
62679             this.activeEditor.completeEdit();
62680         }
62681         this.activeEditor = null;
62682     },
62683         
62684          /**
62685      * Called to get grid's drag proxy text, by default returns this.ddText.
62686      * @return {String}
62687      */
62688     getDragDropText : function(){
62689         var count = this.selModel.getSelectedCell() ? 1 : 0;
62690         return String.format(this.ddText, count, count == 1 ? '' : 's');
62691     }
62692         
62693 });/*
62694  * Based on:
62695  * Ext JS Library 1.1.1
62696  * Copyright(c) 2006-2007, Ext JS, LLC.
62697  *
62698  * Originally Released Under LGPL - original licence link has changed is not relivant.
62699  *
62700  * Fork - LGPL
62701  * <script type="text/javascript">
62702  */
62703
62704 // private - not really -- you end up using it !
62705 // This is a support class used internally by the Grid components
62706
62707 /**
62708  * @class Roo.grid.GridEditor
62709  * @extends Roo.Editor
62710  * Class for creating and editable grid elements.
62711  * @param {Object} config any settings (must include field)
62712  */
62713 Roo.grid.GridEditor = function(field, config){
62714     if (!config && field.field) {
62715         config = field;
62716         field = Roo.factory(config.field, Roo.form);
62717     }
62718     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62719     field.monitorTab = false;
62720 };
62721
62722 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62723     
62724     /**
62725      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62726      */
62727     
62728     alignment: "tl-tl",
62729     autoSize: "width",
62730     hideEl : false,
62731     cls: "x-small-editor x-grid-editor",
62732     shim:false,
62733     shadow:"frame"
62734 });/*
62735  * Based on:
62736  * Ext JS Library 1.1.1
62737  * Copyright(c) 2006-2007, Ext JS, LLC.
62738  *
62739  * Originally Released Under LGPL - original licence link has changed is not relivant.
62740  *
62741  * Fork - LGPL
62742  * <script type="text/javascript">
62743  */
62744   
62745
62746   
62747 Roo.grid.PropertyRecord = Roo.data.Record.create([
62748     {name:'name',type:'string'},  'value'
62749 ]);
62750
62751
62752 Roo.grid.PropertyStore = function(grid, source){
62753     this.grid = grid;
62754     this.store = new Roo.data.Store({
62755         recordType : Roo.grid.PropertyRecord
62756     });
62757     this.store.on('update', this.onUpdate,  this);
62758     if(source){
62759         this.setSource(source);
62760     }
62761     Roo.grid.PropertyStore.superclass.constructor.call(this);
62762 };
62763
62764
62765
62766 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62767     setSource : function(o){
62768         this.source = o;
62769         this.store.removeAll();
62770         var data = [];
62771         for(var k in o){
62772             if(this.isEditableValue(o[k])){
62773                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62774             }
62775         }
62776         this.store.loadRecords({records: data}, {}, true);
62777     },
62778
62779     onUpdate : function(ds, record, type){
62780         if(type == Roo.data.Record.EDIT){
62781             var v = record.data['value'];
62782             var oldValue = record.modified['value'];
62783             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62784                 this.source[record.id] = v;
62785                 record.commit();
62786                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62787             }else{
62788                 record.reject();
62789             }
62790         }
62791     },
62792
62793     getProperty : function(row){
62794        return this.store.getAt(row);
62795     },
62796
62797     isEditableValue: function(val){
62798         if(val && val instanceof Date){
62799             return true;
62800         }else if(typeof val == 'object' || typeof val == 'function'){
62801             return false;
62802         }
62803         return true;
62804     },
62805
62806     setValue : function(prop, value){
62807         this.source[prop] = value;
62808         this.store.getById(prop).set('value', value);
62809     },
62810
62811     getSource : function(){
62812         return this.source;
62813     }
62814 });
62815
62816 Roo.grid.PropertyColumnModel = function(grid, store){
62817     this.grid = grid;
62818     var g = Roo.grid;
62819     g.PropertyColumnModel.superclass.constructor.call(this, [
62820         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62821         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62822     ]);
62823     this.store = store;
62824     this.bselect = Roo.DomHelper.append(document.body, {
62825         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62826             {tag: 'option', value: 'true', html: 'true'},
62827             {tag: 'option', value: 'false', html: 'false'}
62828         ]
62829     });
62830     Roo.id(this.bselect);
62831     var f = Roo.form;
62832     this.editors = {
62833         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62834         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62835         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62836         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62837         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62838     };
62839     this.renderCellDelegate = this.renderCell.createDelegate(this);
62840     this.renderPropDelegate = this.renderProp.createDelegate(this);
62841 };
62842
62843 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62844     
62845     
62846     nameText : 'Name',
62847     valueText : 'Value',
62848     
62849     dateFormat : 'm/j/Y',
62850     
62851     
62852     renderDate : function(dateVal){
62853         return dateVal.dateFormat(this.dateFormat);
62854     },
62855
62856     renderBool : function(bVal){
62857         return bVal ? 'true' : 'false';
62858     },
62859
62860     isCellEditable : function(colIndex, rowIndex){
62861         return colIndex == 1;
62862     },
62863
62864     getRenderer : function(col){
62865         return col == 1 ?
62866             this.renderCellDelegate : this.renderPropDelegate;
62867     },
62868
62869     renderProp : function(v){
62870         return this.getPropertyName(v);
62871     },
62872
62873     renderCell : function(val){
62874         var rv = val;
62875         if(val instanceof Date){
62876             rv = this.renderDate(val);
62877         }else if(typeof val == 'boolean'){
62878             rv = this.renderBool(val);
62879         }
62880         return Roo.util.Format.htmlEncode(rv);
62881     },
62882
62883     getPropertyName : function(name){
62884         var pn = this.grid.propertyNames;
62885         return pn && pn[name] ? pn[name] : name;
62886     },
62887
62888     getCellEditor : function(colIndex, rowIndex){
62889         var p = this.store.getProperty(rowIndex);
62890         var n = p.data['name'], val = p.data['value'];
62891         
62892         if(typeof(this.grid.customEditors[n]) == 'string'){
62893             return this.editors[this.grid.customEditors[n]];
62894         }
62895         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62896             return this.grid.customEditors[n];
62897         }
62898         if(val instanceof Date){
62899             return this.editors['date'];
62900         }else if(typeof val == 'number'){
62901             return this.editors['number'];
62902         }else if(typeof val == 'boolean'){
62903             return this.editors['boolean'];
62904         }else{
62905             return this.editors['string'];
62906         }
62907     }
62908 });
62909
62910 /**
62911  * @class Roo.grid.PropertyGrid
62912  * @extends Roo.grid.EditorGrid
62913  * This class represents the  interface of a component based property grid control.
62914  * <br><br>Usage:<pre><code>
62915  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62916       
62917  });
62918  // set any options
62919  grid.render();
62920  * </code></pre>
62921   
62922  * @constructor
62923  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62924  * The container MUST have some type of size defined for the grid to fill. The container will be
62925  * automatically set to position relative if it isn't already.
62926  * @param {Object} config A config object that sets properties on this grid.
62927  */
62928 Roo.grid.PropertyGrid = function(container, config){
62929     config = config || {};
62930     var store = new Roo.grid.PropertyStore(this);
62931     this.store = store;
62932     var cm = new Roo.grid.PropertyColumnModel(this, store);
62933     store.store.sort('name', 'ASC');
62934     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62935         ds: store.store,
62936         cm: cm,
62937         enableColLock:false,
62938         enableColumnMove:false,
62939         stripeRows:false,
62940         trackMouseOver: false,
62941         clicksToEdit:1
62942     }, config));
62943     this.getGridEl().addClass('x-props-grid');
62944     this.lastEditRow = null;
62945     this.on('columnresize', this.onColumnResize, this);
62946     this.addEvents({
62947          /**
62948              * @event beforepropertychange
62949              * Fires before a property changes (return false to stop?)
62950              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62951              * @param {String} id Record Id
62952              * @param {String} newval New Value
62953          * @param {String} oldval Old Value
62954              */
62955         "beforepropertychange": true,
62956         /**
62957              * @event propertychange
62958              * Fires after a property changes
62959              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62960              * @param {String} id Record Id
62961              * @param {String} newval New Value
62962          * @param {String} oldval Old Value
62963              */
62964         "propertychange": true
62965     });
62966     this.customEditors = this.customEditors || {};
62967 };
62968 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62969     
62970      /**
62971      * @cfg {Object} customEditors map of colnames=> custom editors.
62972      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62973      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62974      * false disables editing of the field.
62975          */
62976     
62977       /**
62978      * @cfg {Object} propertyNames map of property Names to their displayed value
62979          */
62980     
62981     render : function(){
62982         Roo.grid.PropertyGrid.superclass.render.call(this);
62983         this.autoSize.defer(100, this);
62984     },
62985
62986     autoSize : function(){
62987         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62988         if(this.view){
62989             this.view.fitColumns();
62990         }
62991     },
62992
62993     onColumnResize : function(){
62994         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62995         this.autoSize();
62996     },
62997     /**
62998      * Sets the data for the Grid
62999      * accepts a Key => Value object of all the elements avaiable.
63000      * @param {Object} data  to appear in grid.
63001      */
63002     setSource : function(source){
63003         this.store.setSource(source);
63004         //this.autoSize();
63005     },
63006     /**
63007      * Gets all the data from the grid.
63008      * @return {Object} data  data stored in grid
63009      */
63010     getSource : function(){
63011         return this.store.getSource();
63012     }
63013 });/*
63014   
63015  * Licence LGPL
63016  
63017  */
63018  
63019 /**
63020  * @class Roo.grid.Calendar
63021  * @extends Roo.grid.Grid
63022  * This class extends the Grid to provide a calendar widget
63023  * <br><br>Usage:<pre><code>
63024  var grid = new Roo.grid.Calendar("my-container-id", {
63025      ds: myDataStore,
63026      cm: myColModel,
63027      selModel: mySelectionModel,
63028      autoSizeColumns: true,
63029      monitorWindowResize: false,
63030      trackMouseOver: true
63031      eventstore : real data store..
63032  });
63033  // set any options
63034  grid.render();
63035   
63036   * @constructor
63037  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63038  * The container MUST have some type of size defined for the grid to fill. The container will be
63039  * automatically set to position relative if it isn't already.
63040  * @param {Object} config A config object that sets properties on this grid.
63041  */
63042 Roo.grid.Calendar = function(container, config){
63043         // initialize the container
63044         this.container = Roo.get(container);
63045         this.container.update("");
63046         this.container.setStyle("overflow", "hidden");
63047     this.container.addClass('x-grid-container');
63048
63049     this.id = this.container.id;
63050
63051     Roo.apply(this, config);
63052     // check and correct shorthanded configs
63053     
63054     var rows = [];
63055     var d =1;
63056     for (var r = 0;r < 6;r++) {
63057         
63058         rows[r]=[];
63059         for (var c =0;c < 7;c++) {
63060             rows[r][c]= '';
63061         }
63062     }
63063     if (this.eventStore) {
63064         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63065         this.eventStore.on('load',this.onLoad, this);
63066         this.eventStore.on('beforeload',this.clearEvents, this);
63067          
63068     }
63069     
63070     this.dataSource = new Roo.data.Store({
63071             proxy: new Roo.data.MemoryProxy(rows),
63072             reader: new Roo.data.ArrayReader({}, [
63073                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63074     });
63075
63076     this.dataSource.load();
63077     this.ds = this.dataSource;
63078     this.ds.xmodule = this.xmodule || false;
63079     
63080     
63081     var cellRender = function(v,x,r)
63082     {
63083         return String.format(
63084             '<div class="fc-day  fc-widget-content"><div>' +
63085                 '<div class="fc-event-container"></div>' +
63086                 '<div class="fc-day-number">{0}</div>'+
63087                 
63088                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63089             '</div></div>', v);
63090     
63091     }
63092     
63093     
63094     this.colModel = new Roo.grid.ColumnModel( [
63095         {
63096             xtype: 'ColumnModel',
63097             xns: Roo.grid,
63098             dataIndex : 'weekday0',
63099             header : 'Sunday',
63100             renderer : cellRender
63101         },
63102         {
63103             xtype: 'ColumnModel',
63104             xns: Roo.grid,
63105             dataIndex : 'weekday1',
63106             header : 'Monday',
63107             renderer : cellRender
63108         },
63109         {
63110             xtype: 'ColumnModel',
63111             xns: Roo.grid,
63112             dataIndex : 'weekday2',
63113             header : 'Tuesday',
63114             renderer : cellRender
63115         },
63116         {
63117             xtype: 'ColumnModel',
63118             xns: Roo.grid,
63119             dataIndex : 'weekday3',
63120             header : 'Wednesday',
63121             renderer : cellRender
63122         },
63123         {
63124             xtype: 'ColumnModel',
63125             xns: Roo.grid,
63126             dataIndex : 'weekday4',
63127             header : 'Thursday',
63128             renderer : cellRender
63129         },
63130         {
63131             xtype: 'ColumnModel',
63132             xns: Roo.grid,
63133             dataIndex : 'weekday5',
63134             header : 'Friday',
63135             renderer : cellRender
63136         },
63137         {
63138             xtype: 'ColumnModel',
63139             xns: Roo.grid,
63140             dataIndex : 'weekday6',
63141             header : 'Saturday',
63142             renderer : cellRender
63143         }
63144     ]);
63145     this.cm = this.colModel;
63146     this.cm.xmodule = this.xmodule || false;
63147  
63148         
63149           
63150     //this.selModel = new Roo.grid.CellSelectionModel();
63151     //this.sm = this.selModel;
63152     //this.selModel.init(this);
63153     
63154     
63155     if(this.width){
63156         this.container.setWidth(this.width);
63157     }
63158
63159     if(this.height){
63160         this.container.setHeight(this.height);
63161     }
63162     /** @private */
63163         this.addEvents({
63164         // raw events
63165         /**
63166          * @event click
63167          * The raw click event for the entire grid.
63168          * @param {Roo.EventObject} e
63169          */
63170         "click" : true,
63171         /**
63172          * @event dblclick
63173          * The raw dblclick event for the entire grid.
63174          * @param {Roo.EventObject} e
63175          */
63176         "dblclick" : true,
63177         /**
63178          * @event contextmenu
63179          * The raw contextmenu event for the entire grid.
63180          * @param {Roo.EventObject} e
63181          */
63182         "contextmenu" : true,
63183         /**
63184          * @event mousedown
63185          * The raw mousedown event for the entire grid.
63186          * @param {Roo.EventObject} e
63187          */
63188         "mousedown" : true,
63189         /**
63190          * @event mouseup
63191          * The raw mouseup event for the entire grid.
63192          * @param {Roo.EventObject} e
63193          */
63194         "mouseup" : true,
63195         /**
63196          * @event mouseover
63197          * The raw mouseover event for the entire grid.
63198          * @param {Roo.EventObject} e
63199          */
63200         "mouseover" : true,
63201         /**
63202          * @event mouseout
63203          * The raw mouseout event for the entire grid.
63204          * @param {Roo.EventObject} e
63205          */
63206         "mouseout" : true,
63207         /**
63208          * @event keypress
63209          * The raw keypress event for the entire grid.
63210          * @param {Roo.EventObject} e
63211          */
63212         "keypress" : true,
63213         /**
63214          * @event keydown
63215          * The raw keydown event for the entire grid.
63216          * @param {Roo.EventObject} e
63217          */
63218         "keydown" : true,
63219
63220         // custom events
63221
63222         /**
63223          * @event cellclick
63224          * Fires when a cell is clicked
63225          * @param {Grid} this
63226          * @param {Number} rowIndex
63227          * @param {Number} columnIndex
63228          * @param {Roo.EventObject} e
63229          */
63230         "cellclick" : true,
63231         /**
63232          * @event celldblclick
63233          * Fires when a cell is double clicked
63234          * @param {Grid} this
63235          * @param {Number} rowIndex
63236          * @param {Number} columnIndex
63237          * @param {Roo.EventObject} e
63238          */
63239         "celldblclick" : true,
63240         /**
63241          * @event rowclick
63242          * Fires when a row is clicked
63243          * @param {Grid} this
63244          * @param {Number} rowIndex
63245          * @param {Roo.EventObject} e
63246          */
63247         "rowclick" : true,
63248         /**
63249          * @event rowdblclick
63250          * Fires when a row is double clicked
63251          * @param {Grid} this
63252          * @param {Number} rowIndex
63253          * @param {Roo.EventObject} e
63254          */
63255         "rowdblclick" : true,
63256         /**
63257          * @event headerclick
63258          * Fires when a header is clicked
63259          * @param {Grid} this
63260          * @param {Number} columnIndex
63261          * @param {Roo.EventObject} e
63262          */
63263         "headerclick" : true,
63264         /**
63265          * @event headerdblclick
63266          * Fires when a header cell is double clicked
63267          * @param {Grid} this
63268          * @param {Number} columnIndex
63269          * @param {Roo.EventObject} e
63270          */
63271         "headerdblclick" : true,
63272         /**
63273          * @event rowcontextmenu
63274          * Fires when a row is right clicked
63275          * @param {Grid} this
63276          * @param {Number} rowIndex
63277          * @param {Roo.EventObject} e
63278          */
63279         "rowcontextmenu" : true,
63280         /**
63281          * @event cellcontextmenu
63282          * Fires when a cell is right clicked
63283          * @param {Grid} this
63284          * @param {Number} rowIndex
63285          * @param {Number} cellIndex
63286          * @param {Roo.EventObject} e
63287          */
63288          "cellcontextmenu" : true,
63289         /**
63290          * @event headercontextmenu
63291          * Fires when a header is right clicked
63292          * @param {Grid} this
63293          * @param {Number} columnIndex
63294          * @param {Roo.EventObject} e
63295          */
63296         "headercontextmenu" : true,
63297         /**
63298          * @event bodyscroll
63299          * Fires when the body element is scrolled
63300          * @param {Number} scrollLeft
63301          * @param {Number} scrollTop
63302          */
63303         "bodyscroll" : true,
63304         /**
63305          * @event columnresize
63306          * Fires when the user resizes a column
63307          * @param {Number} columnIndex
63308          * @param {Number} newSize
63309          */
63310         "columnresize" : true,
63311         /**
63312          * @event columnmove
63313          * Fires when the user moves a column
63314          * @param {Number} oldIndex
63315          * @param {Number} newIndex
63316          */
63317         "columnmove" : true,
63318         /**
63319          * @event startdrag
63320          * Fires when row(s) start being dragged
63321          * @param {Grid} this
63322          * @param {Roo.GridDD} dd The drag drop object
63323          * @param {event} e The raw browser event
63324          */
63325         "startdrag" : true,
63326         /**
63327          * @event enddrag
63328          * Fires when a drag operation is complete
63329          * @param {Grid} this
63330          * @param {Roo.GridDD} dd The drag drop object
63331          * @param {event} e The raw browser event
63332          */
63333         "enddrag" : true,
63334         /**
63335          * @event dragdrop
63336          * Fires when dragged row(s) are dropped on a valid DD target
63337          * @param {Grid} this
63338          * @param {Roo.GridDD} dd The drag drop object
63339          * @param {String} targetId The target drag drop object
63340          * @param {event} e The raw browser event
63341          */
63342         "dragdrop" : true,
63343         /**
63344          * @event dragover
63345          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63346          * @param {Grid} this
63347          * @param {Roo.GridDD} dd The drag drop object
63348          * @param {String} targetId The target drag drop object
63349          * @param {event} e The raw browser event
63350          */
63351         "dragover" : true,
63352         /**
63353          * @event dragenter
63354          *  Fires when the dragged row(s) first cross another DD target while being dragged
63355          * @param {Grid} this
63356          * @param {Roo.GridDD} dd The drag drop object
63357          * @param {String} targetId The target drag drop object
63358          * @param {event} e The raw browser event
63359          */
63360         "dragenter" : true,
63361         /**
63362          * @event dragout
63363          * Fires when the dragged row(s) leave another DD target while being dragged
63364          * @param {Grid} this
63365          * @param {Roo.GridDD} dd The drag drop object
63366          * @param {String} targetId The target drag drop object
63367          * @param {event} e The raw browser event
63368          */
63369         "dragout" : true,
63370         /**
63371          * @event rowclass
63372          * Fires when a row is rendered, so you can change add a style to it.
63373          * @param {GridView} gridview   The grid view
63374          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63375          */
63376         'rowclass' : true,
63377
63378         /**
63379          * @event render
63380          * Fires when the grid is rendered
63381          * @param {Grid} grid
63382          */
63383         'render' : true,
63384             /**
63385              * @event select
63386              * Fires when a date is selected
63387              * @param {DatePicker} this
63388              * @param {Date} date The selected date
63389              */
63390         'select': true,
63391         /**
63392              * @event monthchange
63393              * Fires when the displayed month changes 
63394              * @param {DatePicker} this
63395              * @param {Date} date The selected month
63396              */
63397         'monthchange': true,
63398         /**
63399              * @event evententer
63400              * Fires when mouse over an event
63401              * @param {Calendar} this
63402              * @param {event} Event
63403              */
63404         'evententer': true,
63405         /**
63406              * @event eventleave
63407              * Fires when the mouse leaves an
63408              * @param {Calendar} this
63409              * @param {event}
63410              */
63411         'eventleave': true,
63412         /**
63413              * @event eventclick
63414              * Fires when the mouse click an
63415              * @param {Calendar} this
63416              * @param {event}
63417              */
63418         'eventclick': true,
63419         /**
63420              * @event eventrender
63421              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63422              * @param {Calendar} this
63423              * @param {data} data to be modified
63424              */
63425         'eventrender': true
63426         
63427     });
63428
63429     Roo.grid.Grid.superclass.constructor.call(this);
63430     this.on('render', function() {
63431         this.view.el.addClass('x-grid-cal'); 
63432         
63433         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63434
63435     },this);
63436     
63437     if (!Roo.grid.Calendar.style) {
63438         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63439             
63440             
63441             '.x-grid-cal .x-grid-col' :  {
63442                 height: 'auto !important',
63443                 'vertical-align': 'top'
63444             },
63445             '.x-grid-cal  .fc-event-hori' : {
63446                 height: '14px'
63447             }
63448              
63449             
63450         }, Roo.id());
63451     }
63452
63453     
63454     
63455 };
63456 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63457     /**
63458      * @cfg {Store} eventStore The store that loads events.
63459      */
63460     eventStore : 25,
63461
63462      
63463     activeDate : false,
63464     startDay : 0,
63465     autoWidth : true,
63466     monitorWindowResize : false,
63467
63468     
63469     resizeColumns : function() {
63470         var col = (this.view.el.getWidth() / 7) - 3;
63471         // loop through cols, and setWidth
63472         for(var i =0 ; i < 7 ; i++){
63473             this.cm.setColumnWidth(i, col);
63474         }
63475     },
63476      setDate :function(date) {
63477         
63478         Roo.log('setDate?');
63479         
63480         this.resizeColumns();
63481         var vd = this.activeDate;
63482         this.activeDate = date;
63483 //        if(vd && this.el){
63484 //            var t = date.getTime();
63485 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63486 //                Roo.log('using add remove');
63487 //                
63488 //                this.fireEvent('monthchange', this, date);
63489 //                
63490 //                this.cells.removeClass("fc-state-highlight");
63491 //                this.cells.each(function(c){
63492 //                   if(c.dateValue == t){
63493 //                       c.addClass("fc-state-highlight");
63494 //                       setTimeout(function(){
63495 //                            try{c.dom.firstChild.focus();}catch(e){}
63496 //                       }, 50);
63497 //                       return false;
63498 //                   }
63499 //                   return true;
63500 //                });
63501 //                return;
63502 //            }
63503 //        }
63504         
63505         var days = date.getDaysInMonth();
63506         
63507         var firstOfMonth = date.getFirstDateOfMonth();
63508         var startingPos = firstOfMonth.getDay()-this.startDay;
63509         
63510         if(startingPos < this.startDay){
63511             startingPos += 7;
63512         }
63513         
63514         var pm = date.add(Date.MONTH, -1);
63515         var prevStart = pm.getDaysInMonth()-startingPos;
63516 //        
63517         
63518         
63519         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63520         
63521         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63522         //this.cells.addClassOnOver('fc-state-hover');
63523         
63524         var cells = this.cells.elements;
63525         var textEls = this.textNodes;
63526         
63527         //Roo.each(cells, function(cell){
63528         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63529         //});
63530         
63531         days += startingPos;
63532
63533         // convert everything to numbers so it's fast
63534         var day = 86400000;
63535         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63536         //Roo.log(d);
63537         //Roo.log(pm);
63538         //Roo.log(prevStart);
63539         
63540         var today = new Date().clearTime().getTime();
63541         var sel = date.clearTime().getTime();
63542         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63543         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63544         var ddMatch = this.disabledDatesRE;
63545         var ddText = this.disabledDatesText;
63546         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63547         var ddaysText = this.disabledDaysText;
63548         var format = this.format;
63549         
63550         var setCellClass = function(cal, cell){
63551             
63552             //Roo.log('set Cell Class');
63553             cell.title = "";
63554             var t = d.getTime();
63555             
63556             //Roo.log(d);
63557             
63558             
63559             cell.dateValue = t;
63560             if(t == today){
63561                 cell.className += " fc-today";
63562                 cell.className += " fc-state-highlight";
63563                 cell.title = cal.todayText;
63564             }
63565             if(t == sel){
63566                 // disable highlight in other month..
63567                 cell.className += " fc-state-highlight";
63568                 
63569             }
63570             // disabling
63571             if(t < min) {
63572                 //cell.className = " fc-state-disabled";
63573                 cell.title = cal.minText;
63574                 return;
63575             }
63576             if(t > max) {
63577                 //cell.className = " fc-state-disabled";
63578                 cell.title = cal.maxText;
63579                 return;
63580             }
63581             if(ddays){
63582                 if(ddays.indexOf(d.getDay()) != -1){
63583                     // cell.title = ddaysText;
63584                    // cell.className = " fc-state-disabled";
63585                 }
63586             }
63587             if(ddMatch && format){
63588                 var fvalue = d.dateFormat(format);
63589                 if(ddMatch.test(fvalue)){
63590                     cell.title = ddText.replace("%0", fvalue);
63591                    cell.className = " fc-state-disabled";
63592                 }
63593             }
63594             
63595             if (!cell.initialClassName) {
63596                 cell.initialClassName = cell.dom.className;
63597             }
63598             
63599             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63600         };
63601
63602         var i = 0;
63603         
63604         for(; i < startingPos; i++) {
63605             cells[i].dayName =  (++prevStart);
63606             Roo.log(textEls[i]);
63607             d.setDate(d.getDate()+1);
63608             
63609             //cells[i].className = "fc-past fc-other-month";
63610             setCellClass(this, cells[i]);
63611         }
63612         
63613         var intDay = 0;
63614         
63615         for(; i < days; i++){
63616             intDay = i - startingPos + 1;
63617             cells[i].dayName =  (intDay);
63618             d.setDate(d.getDate()+1);
63619             
63620             cells[i].className = ''; // "x-date-active";
63621             setCellClass(this, cells[i]);
63622         }
63623         var extraDays = 0;
63624         
63625         for(; i < 42; i++) {
63626             //textEls[i].innerHTML = (++extraDays);
63627             
63628             d.setDate(d.getDate()+1);
63629             cells[i].dayName = (++extraDays);
63630             cells[i].className = "fc-future fc-other-month";
63631             setCellClass(this, cells[i]);
63632         }
63633         
63634         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63635         
63636         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63637         
63638         // this will cause all the cells to mis
63639         var rows= [];
63640         var i =0;
63641         for (var r = 0;r < 6;r++) {
63642             for (var c =0;c < 7;c++) {
63643                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63644             }    
63645         }
63646         
63647         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63648         for(i=0;i<cells.length;i++) {
63649             
63650             this.cells.elements[i].dayName = cells[i].dayName ;
63651             this.cells.elements[i].className = cells[i].className;
63652             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63653             this.cells.elements[i].title = cells[i].title ;
63654             this.cells.elements[i].dateValue = cells[i].dateValue ;
63655         }
63656         
63657         
63658         
63659         
63660         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63661         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63662         
63663         ////if(totalRows != 6){
63664             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63665            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63666        // }
63667         
63668         this.fireEvent('monthchange', this, date);
63669         
63670         
63671     },
63672  /**
63673      * Returns the grid's SelectionModel.
63674      * @return {SelectionModel}
63675      */
63676     getSelectionModel : function(){
63677         if(!this.selModel){
63678             this.selModel = new Roo.grid.CellSelectionModel();
63679         }
63680         return this.selModel;
63681     },
63682
63683     load: function() {
63684         this.eventStore.load()
63685         
63686         
63687         
63688     },
63689     
63690     findCell : function(dt) {
63691         dt = dt.clearTime().getTime();
63692         var ret = false;
63693         this.cells.each(function(c){
63694             //Roo.log("check " +c.dateValue + '?=' + dt);
63695             if(c.dateValue == dt){
63696                 ret = c;
63697                 return false;
63698             }
63699             return true;
63700         });
63701         
63702         return ret;
63703     },
63704     
63705     findCells : function(rec) {
63706         var s = rec.data.start_dt.clone().clearTime().getTime();
63707        // Roo.log(s);
63708         var e= rec.data.end_dt.clone().clearTime().getTime();
63709        // Roo.log(e);
63710         var ret = [];
63711         this.cells.each(function(c){
63712              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63713             
63714             if(c.dateValue > e){
63715                 return ;
63716             }
63717             if(c.dateValue < s){
63718                 return ;
63719             }
63720             ret.push(c);
63721         });
63722         
63723         return ret;    
63724     },
63725     
63726     findBestRow: function(cells)
63727     {
63728         var ret = 0;
63729         
63730         for (var i =0 ; i < cells.length;i++) {
63731             ret  = Math.max(cells[i].rows || 0,ret);
63732         }
63733         return ret;
63734         
63735     },
63736     
63737     
63738     addItem : function(rec)
63739     {
63740         // look for vertical location slot in
63741         var cells = this.findCells(rec);
63742         
63743         rec.row = this.findBestRow(cells);
63744         
63745         // work out the location.
63746         
63747         var crow = false;
63748         var rows = [];
63749         for(var i =0; i < cells.length; i++) {
63750             if (!crow) {
63751                 crow = {
63752                     start : cells[i],
63753                     end :  cells[i]
63754                 };
63755                 continue;
63756             }
63757             if (crow.start.getY() == cells[i].getY()) {
63758                 // on same row.
63759                 crow.end = cells[i];
63760                 continue;
63761             }
63762             // different row.
63763             rows.push(crow);
63764             crow = {
63765                 start: cells[i],
63766                 end : cells[i]
63767             };
63768             
63769         }
63770         
63771         rows.push(crow);
63772         rec.els = [];
63773         rec.rows = rows;
63774         rec.cells = cells;
63775         for (var i = 0; i < cells.length;i++) {
63776             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63777             
63778         }
63779         
63780         
63781     },
63782     
63783     clearEvents: function() {
63784         
63785         if (!this.eventStore.getCount()) {
63786             return;
63787         }
63788         // reset number of rows in cells.
63789         Roo.each(this.cells.elements, function(c){
63790             c.rows = 0;
63791         });
63792         
63793         this.eventStore.each(function(e) {
63794             this.clearEvent(e);
63795         },this);
63796         
63797     },
63798     
63799     clearEvent : function(ev)
63800     {
63801         if (ev.els) {
63802             Roo.each(ev.els, function(el) {
63803                 el.un('mouseenter' ,this.onEventEnter, this);
63804                 el.un('mouseleave' ,this.onEventLeave, this);
63805                 el.remove();
63806             },this);
63807             ev.els = [];
63808         }
63809     },
63810     
63811     
63812     renderEvent : function(ev,ctr) {
63813         if (!ctr) {
63814              ctr = this.view.el.select('.fc-event-container',true).first();
63815         }
63816         
63817          
63818         this.clearEvent(ev);
63819             //code
63820        
63821         
63822         
63823         ev.els = [];
63824         var cells = ev.cells;
63825         var rows = ev.rows;
63826         this.fireEvent('eventrender', this, ev);
63827         
63828         for(var i =0; i < rows.length; i++) {
63829             
63830             cls = '';
63831             if (i == 0) {
63832                 cls += ' fc-event-start';
63833             }
63834             if ((i+1) == rows.length) {
63835                 cls += ' fc-event-end';
63836             }
63837             
63838             //Roo.log(ev.data);
63839             // how many rows should it span..
63840             var cg = this.eventTmpl.append(ctr,Roo.apply({
63841                 fccls : cls
63842                 
63843             }, ev.data) , true);
63844             
63845             
63846             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63847             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63848             cg.on('click', this.onEventClick, this, ev);
63849             
63850             ev.els.push(cg);
63851             
63852             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63853             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63854             //Roo.log(cg);
63855              
63856             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63857             cg.setWidth(ebox.right - sbox.x -2);
63858         }
63859     },
63860     
63861     renderEvents: function()
63862     {   
63863         // first make sure there is enough space..
63864         
63865         if (!this.eventTmpl) {
63866             this.eventTmpl = new Roo.Template(
63867                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63868                     '<div class="fc-event-inner">' +
63869                         '<span class="fc-event-time">{time}</span>' +
63870                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63871                     '</div>' +
63872                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63873                 '</div>'
63874             );
63875                 
63876         }
63877                
63878         
63879         
63880         this.cells.each(function(c) {
63881             //Roo.log(c.select('.fc-day-content div',true).first());
63882             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63883         });
63884         
63885         var ctr = this.view.el.select('.fc-event-container',true).first();
63886         
63887         var cls;
63888         this.eventStore.each(function(ev){
63889             
63890             this.renderEvent(ev);
63891              
63892              
63893         }, this);
63894         this.view.layout();
63895         
63896     },
63897     
63898     onEventEnter: function (e, el,event,d) {
63899         this.fireEvent('evententer', this, el, event);
63900     },
63901     
63902     onEventLeave: function (e, el,event,d) {
63903         this.fireEvent('eventleave', this, el, event);
63904     },
63905     
63906     onEventClick: function (e, el,event,d) {
63907         this.fireEvent('eventclick', this, el, event);
63908     },
63909     
63910     onMonthChange: function () {
63911         this.store.load();
63912     },
63913     
63914     onLoad: function () {
63915         
63916         //Roo.log('calendar onload');
63917 //         
63918         if(this.eventStore.getCount() > 0){
63919             
63920            
63921             
63922             this.eventStore.each(function(d){
63923                 
63924                 
63925                 // FIXME..
63926                 var add =   d.data;
63927                 if (typeof(add.end_dt) == 'undefined')  {
63928                     Roo.log("Missing End time in calendar data: ");
63929                     Roo.log(d);
63930                     return;
63931                 }
63932                 if (typeof(add.start_dt) == 'undefined')  {
63933                     Roo.log("Missing Start time in calendar data: ");
63934                     Roo.log(d);
63935                     return;
63936                 }
63937                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63938                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63939                 add.id = add.id || d.id;
63940                 add.title = add.title || '??';
63941                 
63942                 this.addItem(d);
63943                 
63944              
63945             },this);
63946         }
63947         
63948         this.renderEvents();
63949     }
63950     
63951
63952 });
63953 /*
63954  grid : {
63955                 xtype: 'Grid',
63956                 xns: Roo.grid,
63957                 listeners : {
63958                     render : function ()
63959                     {
63960                         _this.grid = this;
63961                         
63962                         if (!this.view.el.hasClass('course-timesheet')) {
63963                             this.view.el.addClass('course-timesheet');
63964                         }
63965                         if (this.tsStyle) {
63966                             this.ds.load({});
63967                             return; 
63968                         }
63969                         Roo.log('width');
63970                         Roo.log(_this.grid.view.el.getWidth());
63971                         
63972                         
63973                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63974                             '.course-timesheet .x-grid-row' : {
63975                                 height: '80px'
63976                             },
63977                             '.x-grid-row td' : {
63978                                 'vertical-align' : 0
63979                             },
63980                             '.course-edit-link' : {
63981                                 'color' : 'blue',
63982                                 'text-overflow' : 'ellipsis',
63983                                 'overflow' : 'hidden',
63984                                 'white-space' : 'nowrap',
63985                                 'cursor' : 'pointer'
63986                             },
63987                             '.sub-link' : {
63988                                 'color' : 'green'
63989                             },
63990                             '.de-act-sup-link' : {
63991                                 'color' : 'purple',
63992                                 'text-decoration' : 'line-through'
63993                             },
63994                             '.de-act-link' : {
63995                                 'color' : 'red',
63996                                 'text-decoration' : 'line-through'
63997                             },
63998                             '.course-timesheet .course-highlight' : {
63999                                 'border-top-style': 'dashed !important',
64000                                 'border-bottom-bottom': 'dashed !important'
64001                             },
64002                             '.course-timesheet .course-item' : {
64003                                 'font-family'   : 'tahoma, arial, helvetica',
64004                                 'font-size'     : '11px',
64005                                 'overflow'      : 'hidden',
64006                                 'padding-left'  : '10px',
64007                                 'padding-right' : '10px',
64008                                 'padding-top' : '10px' 
64009                             }
64010                             
64011                         }, Roo.id());
64012                                 this.ds.load({});
64013                     }
64014                 },
64015                 autoWidth : true,
64016                 monitorWindowResize : false,
64017                 cellrenderer : function(v,x,r)
64018                 {
64019                     return v;
64020                 },
64021                 sm : {
64022                     xtype: 'CellSelectionModel',
64023                     xns: Roo.grid
64024                 },
64025                 dataSource : {
64026                     xtype: 'Store',
64027                     xns: Roo.data,
64028                     listeners : {
64029                         beforeload : function (_self, options)
64030                         {
64031                             options.params = options.params || {};
64032                             options.params._month = _this.monthField.getValue();
64033                             options.params.limit = 9999;
64034                             options.params['sort'] = 'when_dt';    
64035                             options.params['dir'] = 'ASC';    
64036                             this.proxy.loadResponse = this.loadResponse;
64037                             Roo.log("load?");
64038                             //this.addColumns();
64039                         },
64040                         load : function (_self, records, options)
64041                         {
64042                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64043                                 // if you click on the translation.. you can edit it...
64044                                 var el = Roo.get(this);
64045                                 var id = el.dom.getAttribute('data-id');
64046                                 var d = el.dom.getAttribute('data-date');
64047                                 var t = el.dom.getAttribute('data-time');
64048                                 //var id = this.child('span').dom.textContent;
64049                                 
64050                                 //Roo.log(this);
64051                                 Pman.Dialog.CourseCalendar.show({
64052                                     id : id,
64053                                     when_d : d,
64054                                     when_t : t,
64055                                     productitem_active : id ? 1 : 0
64056                                 }, function() {
64057                                     _this.grid.ds.load({});
64058                                 });
64059                            
64060                            });
64061                            
64062                            _this.panel.fireEvent('resize', [ '', '' ]);
64063                         }
64064                     },
64065                     loadResponse : function(o, success, response){
64066                             // this is overridden on before load..
64067                             
64068                             Roo.log("our code?");       
64069                             //Roo.log(success);
64070                             //Roo.log(response)
64071                             delete this.activeRequest;
64072                             if(!success){
64073                                 this.fireEvent("loadexception", this, o, response);
64074                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64075                                 return;
64076                             }
64077                             var result;
64078                             try {
64079                                 result = o.reader.read(response);
64080                             }catch(e){
64081                                 Roo.log("load exception?");
64082                                 this.fireEvent("loadexception", this, o, response, e);
64083                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64084                                 return;
64085                             }
64086                             Roo.log("ready...");        
64087                             // loop through result.records;
64088                             // and set this.tdate[date] = [] << array of records..
64089                             _this.tdata  = {};
64090                             Roo.each(result.records, function(r){
64091                                 //Roo.log(r.data);
64092                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64093                                     _this.tdata[r.data.when_dt.format('j')] = [];
64094                                 }
64095                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64096                             });
64097                             
64098                             //Roo.log(_this.tdata);
64099                             
64100                             result.records = [];
64101                             result.totalRecords = 6;
64102                     
64103                             // let's generate some duumy records for the rows.
64104                             //var st = _this.dateField.getValue();
64105                             
64106                             // work out monday..
64107                             //st = st.add(Date.DAY, -1 * st.format('w'));
64108                             
64109                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64110                             
64111                             var firstOfMonth = date.getFirstDayOfMonth();
64112                             var days = date.getDaysInMonth();
64113                             var d = 1;
64114                             var firstAdded = false;
64115                             for (var i = 0; i < result.totalRecords ; i++) {
64116                                 //var d= st.add(Date.DAY, i);
64117                                 var row = {};
64118                                 var added = 0;
64119                                 for(var w = 0 ; w < 7 ; w++){
64120                                     if(!firstAdded && firstOfMonth != w){
64121                                         continue;
64122                                     }
64123                                     if(d > days){
64124                                         continue;
64125                                     }
64126                                     firstAdded = true;
64127                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64128                                     row['weekday'+w] = String.format(
64129                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64130                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64131                                                     d,
64132                                                     date.format('Y-m-')+dd
64133                                                 );
64134                                     added++;
64135                                     if(typeof(_this.tdata[d]) != 'undefined'){
64136                                         Roo.each(_this.tdata[d], function(r){
64137                                             var is_sub = '';
64138                                             var deactive = '';
64139                                             var id = r.id;
64140                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64141                                             if(r.parent_id*1>0){
64142                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64143                                                 id = r.parent_id;
64144                                             }
64145                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64146                                                 deactive = 'de-act-link';
64147                                             }
64148                                             
64149                                             row['weekday'+w] += String.format(
64150                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64151                                                     id, //0
64152                                                     r.product_id_name, //1
64153                                                     r.when_dt.format('h:ia'), //2
64154                                                     is_sub, //3
64155                                                     deactive, //4
64156                                                     desc // 5
64157                                             );
64158                                         });
64159                                     }
64160                                     d++;
64161                                 }
64162                                 
64163                                 // only do this if something added..
64164                                 if(added > 0){ 
64165                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64166                                 }
64167                                 
64168                                 
64169                                 // push it twice. (second one with an hour..
64170                                 
64171                             }
64172                             //Roo.log(result);
64173                             this.fireEvent("load", this, o, o.request.arg);
64174                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64175                         },
64176                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64177                     proxy : {
64178                         xtype: 'HttpProxy',
64179                         xns: Roo.data,
64180                         method : 'GET',
64181                         url : baseURL + '/Roo/Shop_course.php'
64182                     },
64183                     reader : {
64184                         xtype: 'JsonReader',
64185                         xns: Roo.data,
64186                         id : 'id',
64187                         fields : [
64188                             {
64189                                 'name': 'id',
64190                                 'type': 'int'
64191                             },
64192                             {
64193                                 'name': 'when_dt',
64194                                 'type': 'string'
64195                             },
64196                             {
64197                                 'name': 'end_dt',
64198                                 'type': 'string'
64199                             },
64200                             {
64201                                 'name': 'parent_id',
64202                                 'type': 'int'
64203                             },
64204                             {
64205                                 'name': 'product_id',
64206                                 'type': 'int'
64207                             },
64208                             {
64209                                 'name': 'productitem_id',
64210                                 'type': 'int'
64211                             },
64212                             {
64213                                 'name': 'guid',
64214                                 'type': 'int'
64215                             }
64216                         ]
64217                     }
64218                 },
64219                 toolbar : {
64220                     xtype: 'Toolbar',
64221                     xns: Roo,
64222                     items : [
64223                         {
64224                             xtype: 'Button',
64225                             xns: Roo.Toolbar,
64226                             listeners : {
64227                                 click : function (_self, e)
64228                                 {
64229                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64230                                     sd.setMonth(sd.getMonth()-1);
64231                                     _this.monthField.setValue(sd.format('Y-m-d'));
64232                                     _this.grid.ds.load({});
64233                                 }
64234                             },
64235                             text : "Back"
64236                         },
64237                         {
64238                             xtype: 'Separator',
64239                             xns: Roo.Toolbar
64240                         },
64241                         {
64242                             xtype: 'MonthField',
64243                             xns: Roo.form,
64244                             listeners : {
64245                                 render : function (_self)
64246                                 {
64247                                     _this.monthField = _self;
64248                                    // _this.monthField.set  today
64249                                 },
64250                                 select : function (combo, date)
64251                                 {
64252                                     _this.grid.ds.load({});
64253                                 }
64254                             },
64255                             value : (function() { return new Date(); })()
64256                         },
64257                         {
64258                             xtype: 'Separator',
64259                             xns: Roo.Toolbar
64260                         },
64261                         {
64262                             xtype: 'TextItem',
64263                             xns: Roo.Toolbar,
64264                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64265                         },
64266                         {
64267                             xtype: 'Fill',
64268                             xns: Roo.Toolbar
64269                         },
64270                         {
64271                             xtype: 'Button',
64272                             xns: Roo.Toolbar,
64273                             listeners : {
64274                                 click : function (_self, e)
64275                                 {
64276                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64277                                     sd.setMonth(sd.getMonth()+1);
64278                                     _this.monthField.setValue(sd.format('Y-m-d'));
64279                                     _this.grid.ds.load({});
64280                                 }
64281                             },
64282                             text : "Next"
64283                         }
64284                     ]
64285                 },
64286                  
64287             }
64288         };
64289         
64290         *//*
64291  * Based on:
64292  * Ext JS Library 1.1.1
64293  * Copyright(c) 2006-2007, Ext JS, LLC.
64294  *
64295  * Originally Released Under LGPL - original licence link has changed is not relivant.
64296  *
64297  * Fork - LGPL
64298  * <script type="text/javascript">
64299  */
64300  
64301 /**
64302  * @class Roo.LoadMask
64303  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64304  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64305  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64306  * element's UpdateManager load indicator and will be destroyed after the initial load.
64307  * @constructor
64308  * Create a new LoadMask
64309  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64310  * @param {Object} config The config object
64311  */
64312 Roo.LoadMask = function(el, config){
64313     this.el = Roo.get(el);
64314     Roo.apply(this, config);
64315     if(this.store){
64316         this.store.on('beforeload', this.onBeforeLoad, this);
64317         this.store.on('load', this.onLoad, this);
64318         this.store.on('loadexception', this.onLoadException, this);
64319         this.removeMask = false;
64320     }else{
64321         var um = this.el.getUpdateManager();
64322         um.showLoadIndicator = false; // disable the default indicator
64323         um.on('beforeupdate', this.onBeforeLoad, this);
64324         um.on('update', this.onLoad, this);
64325         um.on('failure', this.onLoad, this);
64326         this.removeMask = true;
64327     }
64328 };
64329
64330 Roo.LoadMask.prototype = {
64331     /**
64332      * @cfg {Boolean} removeMask
64333      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64334      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64335      */
64336     removeMask : false,
64337     /**
64338      * @cfg {String} msg
64339      * The text to display in a centered loading message box (defaults to 'Loading...')
64340      */
64341     msg : 'Loading...',
64342     /**
64343      * @cfg {String} msgCls
64344      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64345      */
64346     msgCls : 'x-mask-loading',
64347
64348     /**
64349      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64350      * @type Boolean
64351      */
64352     disabled: false,
64353
64354     /**
64355      * Disables the mask to prevent it from being displayed
64356      */
64357     disable : function(){
64358        this.disabled = true;
64359     },
64360
64361     /**
64362      * Enables the mask so that it can be displayed
64363      */
64364     enable : function(){
64365         this.disabled = false;
64366     },
64367     
64368     onLoadException : function()
64369     {
64370         Roo.log(arguments);
64371         
64372         if (typeof(arguments[3]) != 'undefined') {
64373             Roo.MessageBox.alert("Error loading",arguments[3]);
64374         } 
64375         /*
64376         try {
64377             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64378                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64379             }   
64380         } catch(e) {
64381             
64382         }
64383         */
64384     
64385         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64386     },
64387     // private
64388     onLoad : function()
64389     {
64390         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64391     },
64392
64393     // private
64394     onBeforeLoad : function(){
64395         if(!this.disabled){
64396             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64397         }
64398     },
64399
64400     // private
64401     destroy : function(){
64402         if(this.store){
64403             this.store.un('beforeload', this.onBeforeLoad, this);
64404             this.store.un('load', this.onLoad, this);
64405             this.store.un('loadexception', this.onLoadException, this);
64406         }else{
64407             var um = this.el.getUpdateManager();
64408             um.un('beforeupdate', this.onBeforeLoad, this);
64409             um.un('update', this.onLoad, this);
64410             um.un('failure', this.onLoad, this);
64411         }
64412     }
64413 };/*
64414  * Based on:
64415  * Ext JS Library 1.1.1
64416  * Copyright(c) 2006-2007, Ext JS, LLC.
64417  *
64418  * Originally Released Under LGPL - original licence link has changed is not relivant.
64419  *
64420  * Fork - LGPL
64421  * <script type="text/javascript">
64422  */
64423
64424
64425 /**
64426  * @class Roo.XTemplate
64427  * @extends Roo.Template
64428  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64429 <pre><code>
64430 var t = new Roo.XTemplate(
64431         '&lt;select name="{name}"&gt;',
64432                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64433         '&lt;/select&gt;'
64434 );
64435  
64436 // then append, applying the master template values
64437  </code></pre>
64438  *
64439  * Supported features:
64440  *
64441  *  Tags:
64442
64443 <pre><code>
64444       {a_variable} - output encoded.
64445       {a_variable.format:("Y-m-d")} - call a method on the variable
64446       {a_variable:raw} - unencoded output
64447       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64448       {a_variable:this.method_on_template(...)} - call a method on the template object.
64449  
64450 </code></pre>
64451  *  The tpl tag:
64452 <pre><code>
64453         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64454         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64455         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64456         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64457   
64458         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64459         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64460 </code></pre>
64461  *      
64462  */
64463 Roo.XTemplate = function()
64464 {
64465     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64466     if (this.html) {
64467         this.compile();
64468     }
64469 };
64470
64471
64472 Roo.extend(Roo.XTemplate, Roo.Template, {
64473
64474     /**
64475      * The various sub templates
64476      */
64477     tpls : false,
64478     /**
64479      *
64480      * basic tag replacing syntax
64481      * WORD:WORD()
64482      *
64483      * // you can fake an object call by doing this
64484      *  x.t:(test,tesT) 
64485      * 
64486      */
64487     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64488
64489     /**
64490      * compile the template
64491      *
64492      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64493      *
64494      */
64495     compile: function()
64496     {
64497         var s = this.html;
64498      
64499         s = ['<tpl>', s, '</tpl>'].join('');
64500     
64501         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64502             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64503             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64504             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64505             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64506             m,
64507             id     = 0,
64508             tpls   = [];
64509     
64510         while(true == !!(m = s.match(re))){
64511             var forMatch   = m[0].match(nameRe),
64512                 ifMatch   = m[0].match(ifRe),
64513                 execMatch   = m[0].match(execRe),
64514                 namedMatch   = m[0].match(namedRe),
64515                 
64516                 exp  = null, 
64517                 fn   = null,
64518                 exec = null,
64519                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64520                 
64521             if (ifMatch) {
64522                 // if - puts fn into test..
64523                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64524                 if(exp){
64525                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64526                 }
64527             }
64528             
64529             if (execMatch) {
64530                 // exec - calls a function... returns empty if true is  returned.
64531                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64532                 if(exp){
64533                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64534                 }
64535             }
64536             
64537             
64538             if (name) {
64539                 // for = 
64540                 switch(name){
64541                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64542                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64543                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64544                 }
64545             }
64546             var uid = namedMatch ? namedMatch[1] : id;
64547             
64548             
64549             tpls.push({
64550                 id:     namedMatch ? namedMatch[1] : id,
64551                 target: name,
64552                 exec:   exec,
64553                 test:   fn,
64554                 body:   m[1] || ''
64555             });
64556             if (namedMatch) {
64557                 s = s.replace(m[0], '');
64558             } else { 
64559                 s = s.replace(m[0], '{xtpl'+ id + '}');
64560             }
64561             ++id;
64562         }
64563         this.tpls = [];
64564         for(var i = tpls.length-1; i >= 0; --i){
64565             this.compileTpl(tpls[i]);
64566             this.tpls[tpls[i].id] = tpls[i];
64567         }
64568         this.master = tpls[tpls.length-1];
64569         return this;
64570     },
64571     /**
64572      * same as applyTemplate, except it's done to one of the subTemplates
64573      * when using named templates, you can do:
64574      *
64575      * var str = pl.applySubTemplate('your-name', values);
64576      *
64577      * 
64578      * @param {Number} id of the template
64579      * @param {Object} values to apply to template
64580      * @param {Object} parent (normaly the instance of this object)
64581      */
64582     applySubTemplate : function(id, values, parent)
64583     {
64584         
64585         
64586         var t = this.tpls[id];
64587         
64588         
64589         try { 
64590             if(t.test && !t.test.call(this, values, parent)){
64591                 return '';
64592             }
64593         } catch(e) {
64594             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64595             Roo.log(e.toString());
64596             Roo.log(t.test);
64597             return ''
64598         }
64599         try { 
64600             
64601             if(t.exec && t.exec.call(this, values, parent)){
64602                 return '';
64603             }
64604         } catch(e) {
64605             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64606             Roo.log(e.toString());
64607             Roo.log(t.exec);
64608             return ''
64609         }
64610         try {
64611             var vs = t.target ? t.target.call(this, values, parent) : values;
64612             parent = t.target ? values : parent;
64613             if(t.target && vs instanceof Array){
64614                 var buf = [];
64615                 for(var i = 0, len = vs.length; i < len; i++){
64616                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64617                 }
64618                 return buf.join('');
64619             }
64620             return t.compiled.call(this, vs, parent);
64621         } catch (e) {
64622             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64623             Roo.log(e.toString());
64624             Roo.log(t.compiled);
64625             return '';
64626         }
64627     },
64628
64629     compileTpl : function(tpl)
64630     {
64631         var fm = Roo.util.Format;
64632         var useF = this.disableFormats !== true;
64633         var sep = Roo.isGecko ? "+" : ",";
64634         var undef = function(str) {
64635             Roo.log("Property not found :"  + str);
64636             return '';
64637         };
64638         
64639         var fn = function(m, name, format, args)
64640         {
64641             //Roo.log(arguments);
64642             args = args ? args.replace(/\\'/g,"'") : args;
64643             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64644             if (typeof(format) == 'undefined') {
64645                 format= 'htmlEncode';
64646             }
64647             if (format == 'raw' ) {
64648                 format = false;
64649             }
64650             
64651             if(name.substr(0, 4) == 'xtpl'){
64652                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64653             }
64654             
64655             // build an array of options to determine if value is undefined..
64656             
64657             // basically get 'xxxx.yyyy' then do
64658             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64659             //    (function () { Roo.log("Property not found"); return ''; })() :
64660             //    ......
64661             
64662             var udef_ar = [];
64663             var lookfor = '';
64664             Roo.each(name.split('.'), function(st) {
64665                 lookfor += (lookfor.length ? '.': '') + st;
64666                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64667             });
64668             
64669             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64670             
64671             
64672             if(format && useF){
64673                 
64674                 args = args ? ',' + args : "";
64675                  
64676                 if(format.substr(0, 5) != "this."){
64677                     format = "fm." + format + '(';
64678                 }else{
64679                     format = 'this.call("'+ format.substr(5) + '", ';
64680                     args = ", values";
64681                 }
64682                 
64683                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64684             }
64685              
64686             if (args.length) {
64687                 // called with xxyx.yuu:(test,test)
64688                 // change to ()
64689                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64690             }
64691             // raw.. - :raw modifier..
64692             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64693             
64694         };
64695         var body;
64696         // branched to use + in gecko and [].join() in others
64697         if(Roo.isGecko){
64698             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64699                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64700                     "';};};";
64701         }else{
64702             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64703             body.push(tpl.body.replace(/(\r\n|\n)/g,
64704                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64705             body.push("'].join('');};};");
64706             body = body.join('');
64707         }
64708         
64709         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64710        
64711         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64712         eval(body);
64713         
64714         return this;
64715     },
64716
64717     applyTemplate : function(values){
64718         return this.master.compiled.call(this, values, {});
64719         //var s = this.subs;
64720     },
64721
64722     apply : function(){
64723         return this.applyTemplate.apply(this, arguments);
64724     }
64725
64726  });
64727
64728 Roo.XTemplate.from = function(el){
64729     el = Roo.getDom(el);
64730     return new Roo.XTemplate(el.value || el.innerHTML);
64731 };