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         var sel = this.core.win.getSelection();
46319         sel.removeAllRanges();
46320         sel.addRange(range);
46321         
46322     
46323         return false;
46324          
46325     }
46326 };
46327      
46328 /**
46329  * @class Roo.htmleditor.Block
46330  * Base class for html editor blocks - do not use it directly .. extend it..
46331  * @cfg {DomElement} node The node to apply stuff to.
46332  * @cfg {String} friendly_name the name that appears in the context bar about this block
46333  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
46334  
46335  * @constructor
46336  * Create a new Filter.
46337  * @param {Object} config Configuration options
46338  */
46339
46340 Roo.htmleditor.Block  = function(cfg)
46341 {
46342     // do nothing .. should not be called really.
46343 }
46344
46345 Roo.htmleditor.Block.factory = function(node)
46346 {
46347     
46348     var id = Roo.get(node).id;
46349     if (typeof(Roo.htmleditor.Block.cache[id]) != 'undefined') {
46350         Roo.htmleditor.Block.cache[id].readElement();
46351         return Roo.htmleditor.Block.cache[id];
46352     }
46353     
46354     var cls = Roo.htmleditor['Block' + node.getAttribute('data-block')];
46355     if (typeof(cls) == 'undefined') {
46356         Roo.log("OOps missing block : " + 'Block' + node.getAttribute('data-block'));
46357         return false;
46358     }
46359     Roo.htmleditor.Block.cache[id] = new cls({ node: node });
46360     return Roo.htmleditor.Block.cache[id];  /// should trigger update element
46361 };
46362 // question goes here... do we need to clear out this cache sometimes?
46363 // or show we make it relivant to the htmleditor.
46364 Roo.htmleditor.Block.cache = {};
46365
46366 Roo.htmleditor.Block.prototype = {
46367     
46368     node : false,
46369     
46370      // used by context menu
46371     friendly_name : 'Image with caption',
46372     
46373     context : false,
46374     /**
46375      * Update a node with values from this object
46376      * @param {DomElement} node
46377      */
46378     updateElement : function(node)
46379     {
46380         Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
46381     },
46382      /**
46383      * convert to plain HTML for calling insertAtCursor..
46384      */
46385     toHTML : function()
46386     {
46387         return Roo.DomHelper.markup(this.toObject());
46388     },
46389     /**
46390      * used by readEleemnt to extract data from a node
46391      * may need improving as it's pretty basic
46392      
46393      * @param {DomElement} node
46394      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46395      * @param {String} attribute (use html - for contents, or style for using next param as style)
46396      * @param {String} style the style property - eg. text-align
46397      */
46398     getVal : function(node, tag, attr, style)
46399     {
46400         var n = node;
46401         if (tag !== true && n.tagName != tag.toUpperCase()) {
46402             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46403             // but kiss for now.
46404             n = node.getElementsByTagName(tag).item(0);
46405         }
46406         if (attr == 'html') {
46407             return n.innerHTML;
46408         }
46409         if (attr == 'style') {
46410             return n.style[style]
46411         }
46412         
46413         return Roo.get(n).attr(attr);
46414             
46415     },
46416     /**
46417      * create a DomHelper friendly object - for use with 
46418      * Roo.DomHelper.markup / overwrite / etc..
46419      * (override this)
46420      */
46421     toObject : function()
46422     {
46423         return {};
46424     },
46425       /**
46426      * Read a node that has a 'data-block' property - and extract the values from it.
46427      * @param {DomElement} node - the node
46428      */
46429     readElement : function(node)
46430     {
46431         
46432     } 
46433     
46434     
46435 };
46436
46437  
46438
46439 /**
46440  * @class Roo.htmleditor.BlockFigure
46441  * Block that has an image and a figcaption
46442  * @cfg {String} image_src the url for the image
46443  * @cfg {String} align (left|right) alignment for the block default left
46444  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46445  * @cfg {String} caption the text to appear below  (and in the alt tag)
46446  * @cfg {String|number} image_width the width of the image number or %?
46447  * @cfg {String|number} image_height the height of the image number or %?
46448  * 
46449  * @constructor
46450  * Create a new Filter.
46451  * @param {Object} config Configuration options
46452  */
46453
46454 Roo.htmleditor.BlockFigure = function(cfg)
46455 {
46456     if (cfg.node) {
46457         this.readElement(cfg.node);
46458         this.updateElement(cfg.node);
46459     }
46460     Roo.apply(this, cfg);
46461 }
46462 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46463  
46464     
46465     // setable values.
46466     image_src: '',
46467     
46468     align: 'left',
46469     caption : '',
46470     text_align: 'left',
46471     
46472     width : '46%',
46473     margin: '2%',
46474     
46475     // used by context menu
46476     friendly_name : 'Image with caption',
46477     
46478     context : { // ?? static really
46479         width : {
46480             title: "Width",
46481             width: 40
46482             // ?? number
46483         },
46484         margin : {
46485             title: "Margin",
46486             width: 40
46487             // ?? number
46488         },
46489         align: {
46490             title: "Align",
46491             opts : [[ "left"],[ "right"]],
46492             width : 80
46493             
46494         },
46495         text_align: {
46496             title: "Caption Align",
46497             opts : [ [ "left"],[ "right"],[ "center"]],
46498             width : 80
46499         },
46500         
46501        
46502         image_src : {
46503             title: "Src",
46504             width: 220
46505         }
46506     },
46507     /**
46508      * create a DomHelper friendly object - for use with
46509      * Roo.DomHelper.markup / overwrite / etc..
46510      */
46511     toObject : function()
46512     {
46513         var d = document.createElement('div');
46514         d.innerHTML = this.caption;
46515         
46516         return {
46517             tag: 'figure',
46518             'data-block' : 'Figure',
46519             contenteditable : 'false',
46520             style : {
46521                 display: 'table',
46522                 float :  this.align ,
46523                 width :  this.width,
46524                 margin:  this.margin
46525             },
46526             cn : [
46527                 {
46528                     tag : 'img',
46529                     src : this.image_src,
46530                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46531                     style: {
46532                         width: '100%'
46533                     }
46534                 },
46535                 {
46536                     tag: 'figcaption',
46537                     contenteditable : true,
46538                     style : {
46539                         'text-align': this.text_align
46540                     },
46541                     html : this.caption
46542                     
46543                 }
46544             ]
46545         };
46546     },
46547     
46548     readElement : function(node)
46549     {
46550         this.image_src = this.getVal(node, 'img', 'src');
46551         this.align = this.getVal(node, 'figure', 'style', 'float');
46552         this.caption = this.getVal(node, 'figcaption', 'html');
46553         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46554         this.width = this.getVal(node, 'figure', 'style', 'width');
46555         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46556         
46557     } 
46558     
46559   
46560    
46561      
46562     
46563     
46564     
46565     
46566 })
46567
46568 //<script type="text/javascript">
46569
46570 /*
46571  * Based  Ext JS Library 1.1.1
46572  * Copyright(c) 2006-2007, Ext JS, LLC.
46573  * LGPL
46574  *
46575  */
46576  
46577 /**
46578  * @class Roo.HtmlEditorCore
46579  * @extends Roo.Component
46580  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46581  *
46582  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46583  */
46584
46585 Roo.HtmlEditorCore = function(config){
46586     
46587     
46588     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46589     
46590     
46591     this.addEvents({
46592         /**
46593          * @event initialize
46594          * Fires when the editor is fully initialized (including the iframe)
46595          * @param {Roo.HtmlEditorCore} this
46596          */
46597         initialize: true,
46598         /**
46599          * @event activate
46600          * Fires when the editor is first receives the focus. Any insertion must wait
46601          * until after this event.
46602          * @param {Roo.HtmlEditorCore} this
46603          */
46604         activate: true,
46605          /**
46606          * @event beforesync
46607          * Fires before the textarea is updated with content from the editor iframe. Return false
46608          * to cancel the sync.
46609          * @param {Roo.HtmlEditorCore} this
46610          * @param {String} html
46611          */
46612         beforesync: true,
46613          /**
46614          * @event beforepush
46615          * Fires before the iframe editor is updated with content from the textarea. Return false
46616          * to cancel the push.
46617          * @param {Roo.HtmlEditorCore} this
46618          * @param {String} html
46619          */
46620         beforepush: true,
46621          /**
46622          * @event sync
46623          * Fires when the textarea is updated with content from the editor iframe.
46624          * @param {Roo.HtmlEditorCore} this
46625          * @param {String} html
46626          */
46627         sync: true,
46628          /**
46629          * @event push
46630          * Fires when the iframe editor is updated with content from the textarea.
46631          * @param {Roo.HtmlEditorCore} this
46632          * @param {String} html
46633          */
46634         push: true,
46635         
46636         /**
46637          * @event editorevent
46638          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46639          * @param {Roo.HtmlEditorCore} this
46640          */
46641         editorevent: true
46642         
46643     });
46644     
46645     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46646     
46647     // defaults : white / black...
46648     this.applyBlacklists();
46649     
46650     
46651     
46652 };
46653
46654
46655 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46656
46657
46658      /**
46659      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46660      */
46661     
46662     owner : false,
46663     
46664      /**
46665      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46666      *                        Roo.resizable.
46667      */
46668     resizable : false,
46669      /**
46670      * @cfg {Number} height (in pixels)
46671      */   
46672     height: 300,
46673    /**
46674      * @cfg {Number} width (in pixels)
46675      */   
46676     width: 500,
46677     
46678     /**
46679      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46680      * 
46681      */
46682     stylesheets: false,
46683     
46684     /**
46685      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46686      */
46687     allowComments: false,
46688     // id of frame..
46689     frameId: false,
46690     
46691     // private properties
46692     validationEvent : false,
46693     deferHeight: true,
46694     initialized : false,
46695     activated : false,
46696     sourceEditMode : false,
46697     onFocus : Roo.emptyFn,
46698     iframePad:3,
46699     hideMode:'offsets',
46700     
46701     clearUp: true,
46702     
46703     // blacklist + whitelisted elements..
46704     black: false,
46705     white: false,
46706      
46707     bodyCls : '',
46708
46709     
46710     undoManager : false,
46711     /**
46712      * Protected method that will not generally be called directly. It
46713      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46714      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46715      */
46716     getDocMarkup : function(){
46717         // body styles..
46718         var st = '';
46719         
46720         // inherit styels from page...?? 
46721         if (this.stylesheets === false) {
46722             
46723             Roo.get(document.head).select('style').each(function(node) {
46724                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46725             });
46726             
46727             Roo.get(document.head).select('link').each(function(node) { 
46728                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46729             });
46730             
46731         } else if (!this.stylesheets.length) {
46732                 // simple..
46733                 st = '<style type="text/css">' +
46734                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46735                    '</style>';
46736         } else {
46737             for (var i in this.stylesheets) {
46738                 if (typeof(this.stylesheets[i]) != 'string') {
46739                     continue;
46740                 }
46741                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46742             }
46743             
46744         }
46745         
46746         st +=  '<style type="text/css">' +
46747             'IMG { cursor: pointer } ' +
46748         '</style>';
46749
46750         var cls = 'roo-htmleditor-body';
46751         
46752         if(this.bodyCls.length){
46753             cls += ' ' + this.bodyCls;
46754         }
46755         
46756         return '<html><head>' + st  +
46757             //<style type="text/css">' +
46758             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46759             //'</style>' +
46760             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46761     },
46762
46763     // private
46764     onRender : function(ct, position)
46765     {
46766         var _t = this;
46767         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46768         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46769         
46770         
46771         this.el.dom.style.border = '0 none';
46772         this.el.dom.setAttribute('tabIndex', -1);
46773         this.el.addClass('x-hidden hide');
46774         
46775         
46776         
46777         if(Roo.isIE){ // fix IE 1px bogus margin
46778             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46779         }
46780        
46781         
46782         this.frameId = Roo.id();
46783         
46784          
46785         
46786         var iframe = this.owner.wrap.createChild({
46787             tag: 'iframe',
46788             cls: 'form-control', // bootstrap..
46789             id: this.frameId,
46790             name: this.frameId,
46791             frameBorder : 'no',
46792             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46793         }, this.el
46794         );
46795         
46796         
46797         this.iframe = iframe.dom;
46798
46799         this.assignDocWin();
46800         
46801         this.doc.designMode = 'on';
46802        
46803         this.doc.open();
46804         this.doc.write(this.getDocMarkup());
46805         this.doc.close();
46806
46807         
46808         var task = { // must defer to wait for browser to be ready
46809             run : function(){
46810                 //console.log("run task?" + this.doc.readyState);
46811                 this.assignDocWin();
46812                 if(this.doc.body || this.doc.readyState == 'complete'){
46813                     try {
46814                         this.doc.designMode="on";
46815                         
46816                     } catch (e) {
46817                         return;
46818                     }
46819                     Roo.TaskMgr.stop(task);
46820                     this.initEditor.defer(10, this);
46821                 }
46822             },
46823             interval : 10,
46824             duration: 10000,
46825             scope: this
46826         };
46827         Roo.TaskMgr.start(task);
46828
46829     },
46830
46831     // private
46832     onResize : function(w, h)
46833     {
46834          Roo.log('resize: ' +w + ',' + h );
46835         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46836         if(!this.iframe){
46837             return;
46838         }
46839         if(typeof w == 'number'){
46840             
46841             this.iframe.style.width = w + 'px';
46842         }
46843         if(typeof h == 'number'){
46844             
46845             this.iframe.style.height = h + 'px';
46846             if(this.doc){
46847                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46848             }
46849         }
46850         
46851     },
46852
46853     /**
46854      * Toggles the editor between standard and source edit mode.
46855      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46856      */
46857     toggleSourceEdit : function(sourceEditMode){
46858         
46859         this.sourceEditMode = sourceEditMode === true;
46860         
46861         if(this.sourceEditMode){
46862  
46863             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46864             
46865         }else{
46866             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46867             //this.iframe.className = '';
46868             this.deferFocus();
46869         }
46870         //this.setSize(this.owner.wrap.getSize());
46871         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46872     },
46873
46874     
46875   
46876
46877     /**
46878      * Protected method that will not generally be called directly. If you need/want
46879      * custom HTML cleanup, this is the method you should override.
46880      * @param {String} html The HTML to be cleaned
46881      * return {String} The cleaned HTML
46882      */
46883     cleanHtml : function(html){
46884         html = String(html);
46885         if(html.length > 5){
46886             if(Roo.isSafari){ // strip safari nonsense
46887                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46888             }
46889         }
46890         if(html == '&nbsp;'){
46891             html = '';
46892         }
46893         return html;
46894     },
46895
46896     /**
46897      * HTML Editor -> Textarea
46898      * Protected method that will not generally be called directly. Syncs the contents
46899      * of the editor iframe with the textarea.
46900      */
46901     syncValue : function()
46902     {
46903         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46904         if(this.initialized){
46905             
46906             this.undoManager.addEvent();
46907
46908             
46909             var bd = (this.doc.body || this.doc.documentElement);
46910             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46911             
46912             // not sure if this is really the place for this
46913             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46914             // this has to update attributes that get duped.. like alt and caption..
46915             
46916             
46917             //Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46918             //     Roo.htmleditor.Block.factory(e);
46919             //},this);
46920             
46921             
46922             var div = document.createElement('div');
46923             div.innerHTML = bd.innerHTML;
46924             // remove content editable. (blocks)
46925             
46926            
46927             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46928             //?? tidy?
46929             var html = div.innerHTML;
46930             if(Roo.isSafari){
46931                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46932                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46933                 if(m && m[1]){
46934                     html = '<div style="'+m[0]+'">' + html + '</div>';
46935                 }
46936             }
46937             html = this.cleanHtml(html);
46938             // fix up the special chars.. normaly like back quotes in word...
46939             // however we do not want to do this with chinese..
46940             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46941                 
46942                 var cc = match.charCodeAt();
46943
46944                 // Get the character value, handling surrogate pairs
46945                 if (match.length == 2) {
46946                     // It's a surrogate pair, calculate the Unicode code point
46947                     var high = match.charCodeAt(0) - 0xD800;
46948                     var low  = match.charCodeAt(1) - 0xDC00;
46949                     cc = (high * 0x400) + low + 0x10000;
46950                 }  else if (
46951                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46952                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46953                     (cc >= 0xf900 && cc < 0xfb00 )
46954                 ) {
46955                         return match;
46956                 }  
46957          
46958                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46959                 return "&#" + cc + ";";
46960                 
46961                 
46962             });
46963             
46964             
46965              
46966             if(this.owner.fireEvent('beforesync', this, html) !== false){
46967                 this.el.dom.value = html;
46968                 this.owner.fireEvent('sync', this, html);
46969             }
46970         }
46971     },
46972
46973     /**
46974      * TEXTAREA -> EDITABLE
46975      * Protected method that will not generally be called directly. Pushes the value of the textarea
46976      * into the iframe editor.
46977      */
46978     pushValue : function()
46979     {
46980         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46981         if(this.initialized){
46982             var v = this.el.dom.value.trim();
46983             
46984             
46985             if(this.owner.fireEvent('beforepush', this, v) !== false){
46986                 var d = (this.doc.body || this.doc.documentElement);
46987                 d.innerHTML = v;
46988                  
46989                 this.el.dom.value = d.innerHTML;
46990                 this.owner.fireEvent('push', this, v);
46991             }
46992             
46993             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46994                 
46995                 Roo.htmleditor.Block.factory(e);
46996                 
46997             },this);
46998             var lc = this.doc.body.lastChild;
46999             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
47000                 // add an extra line at the end.
47001                 this.doc.body.appendChild(this.doc.createElement('br'));
47002             }
47003             
47004             
47005         }
47006     },
47007
47008     // private
47009     deferFocus : function(){
47010         this.focus.defer(10, this);
47011     },
47012
47013     // doc'ed in Field
47014     focus : function(){
47015         if(this.win && !this.sourceEditMode){
47016             this.win.focus();
47017         }else{
47018             this.el.focus();
47019         }
47020     },
47021     
47022     assignDocWin: function()
47023     {
47024         var iframe = this.iframe;
47025         
47026          if(Roo.isIE){
47027             this.doc = iframe.contentWindow.document;
47028             this.win = iframe.contentWindow;
47029         } else {
47030 //            if (!Roo.get(this.frameId)) {
47031 //                return;
47032 //            }
47033 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47034 //            this.win = Roo.get(this.frameId).dom.contentWindow;
47035             
47036             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
47037                 return;
47038             }
47039             
47040             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
47041             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
47042         }
47043     },
47044     
47045     // private
47046     initEditor : function(){
47047         //console.log("INIT EDITOR");
47048         this.assignDocWin();
47049         
47050         
47051         
47052         this.doc.designMode="on";
47053         this.doc.open();
47054         this.doc.write(this.getDocMarkup());
47055         this.doc.close();
47056         
47057         var dbody = (this.doc.body || this.doc.documentElement);
47058         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
47059         // this copies styles from the containing element into thsi one..
47060         // not sure why we need all of this..
47061         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
47062         
47063         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
47064         //ss['background-attachment'] = 'fixed'; // w3c
47065         dbody.bgProperties = 'fixed'; // ie
47066         //Roo.DomHelper.applyStyles(dbody, ss);
47067         Roo.EventManager.on(this.doc, {
47068             //'mousedown': this.onEditorEvent,
47069             'mouseup': this.onEditorEvent,
47070             'dblclick': this.onEditorEvent,
47071             'click': this.onEditorEvent,
47072             'keyup': this.onEditorEvent,
47073             
47074             buffer:100,
47075             scope: this
47076         });
47077         Roo.EventManager.on(this.doc, {
47078             'paste': this.onPasteEvent,
47079             scope : this
47080         });
47081         if(Roo.isGecko){
47082             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
47083         }
47084         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
47085             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
47086         }
47087         this.initialized = true;
47088
47089         
47090         // initialize special key events - enter
47091         new Roo.htmleditor.KeyEnter({core : this});
47092         
47093          
47094         
47095         this.owner.fireEvent('initialize', this);
47096         this.pushValue();
47097     },
47098     
47099     onPasteEvent : function(e,v)
47100     {
47101         // I think we better assume paste is going to be a dirty load of rubish from word..
47102         
47103         // even pasting into a 'email version' of this widget will have to clean up that mess.
47104         var cd = (e.browserEvent.clipboardData || window.clipboardData);
47105         
47106         // check what type of paste - if it's an image, then handle it differently.
47107         if (cd.files.length > 0) {
47108             // pasting images?
47109             var urlAPI = (window.createObjectURL && window) || 
47110                 (window.URL && URL.revokeObjectURL && URL) || 
47111                 (window.webkitURL && webkitURL);
47112     
47113             var url = urlAPI.createObjectURL( cd.files[0]);
47114             this.insertAtCursor('<img src=" + url + ">');
47115             return false;
47116         }
47117         
47118         var html = cd.getData('text/html'); // clipboard event
47119         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
47120         var images = parser.doc ? parser.doc.getElementsByType('pict') : [];
47121         Roo.log(images);
47122         //Roo.log(imgs);
47123         // fixme..
47124         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
47125                        .map(function(g) { return g.toDataURL(); });
47126         
47127         
47128         html = this.cleanWordChars(html);
47129         
47130         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
47131         
47132         if (images.length > 0) {
47133             Roo.each(d.getElementsByTagName('img'), function(img, i) {
47134                 img.setAttribute('src', images[i]);
47135             });
47136         }
47137         
47138       
47139         new Roo.htmleditor.FilterStyleToTag({ node : d });
47140         new Roo.htmleditor.FilterAttributes({
47141             node : d,
47142             attrib_white : ['href', 'src', 'name', 'align'],
47143             attrib_clean : ['href', 'src' ] 
47144         });
47145         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
47146         // should be fonts..
47147         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
47148         new Roo.htmleditor.FilterParagraph({ node : d });
47149         new Roo.htmleditor.FilterSpan({ node : d });
47150         new Roo.htmleditor.FilterLongBr({ node : d });
47151         
47152         
47153         
47154         this.insertAtCursor(d.innerHTML);
47155         
47156         e.preventDefault();
47157         return false;
47158         // default behaveiour should be our local cleanup paste? (optional?)
47159         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
47160         //this.owner.fireEvent('paste', e, v);
47161     },
47162     // private
47163     onDestroy : function(){
47164         
47165         
47166         
47167         if(this.rendered){
47168             
47169             //for (var i =0; i < this.toolbars.length;i++) {
47170             //    // fixme - ask toolbars for heights?
47171             //    this.toolbars[i].onDestroy();
47172            // }
47173             
47174             //this.wrap.dom.innerHTML = '';
47175             //this.wrap.remove();
47176         }
47177     },
47178
47179     // private
47180     onFirstFocus : function(){
47181         
47182         this.assignDocWin();
47183         this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
47184         
47185         this.activated = true;
47186          
47187     
47188         if(Roo.isGecko){ // prevent silly gecko errors
47189             this.win.focus();
47190             var s = this.win.getSelection();
47191             if(!s.focusNode || s.focusNode.nodeType != 3){
47192                 var r = s.getRangeAt(0);
47193                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
47194                 r.collapse(true);
47195                 this.deferFocus();
47196             }
47197             try{
47198                 this.execCmd('useCSS', true);
47199                 this.execCmd('styleWithCSS', false);
47200             }catch(e){}
47201         }
47202         this.owner.fireEvent('activate', this);
47203     },
47204
47205     // private
47206     adjustFont: function(btn){
47207         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
47208         //if(Roo.isSafari){ // safari
47209         //    adjust *= 2;
47210        // }
47211         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
47212         if(Roo.isSafari){ // safari
47213             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
47214             v =  (v < 10) ? 10 : v;
47215             v =  (v > 48) ? 48 : v;
47216             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
47217             
47218         }
47219         
47220         
47221         v = Math.max(1, v+adjust);
47222         
47223         this.execCmd('FontSize', v  );
47224     },
47225
47226     onEditorEvent : function(e)
47227     {
47228         this.owner.fireEvent('editorevent', this, e);
47229       //  this.updateToolbar();
47230         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
47231     },
47232
47233     insertTag : function(tg)
47234     {
47235         // could be a bit smarter... -> wrap the current selected tRoo..
47236         if (tg.toLowerCase() == 'span' ||
47237             tg.toLowerCase() == 'code' ||
47238             tg.toLowerCase() == 'sup' ||
47239             tg.toLowerCase() == 'sub' 
47240             ) {
47241             
47242             range = this.createRange(this.getSelection());
47243             var wrappingNode = this.doc.createElement(tg.toLowerCase());
47244             wrappingNode.appendChild(range.extractContents());
47245             range.insertNode(wrappingNode);
47246
47247             return;
47248             
47249             
47250             
47251         }
47252         this.execCmd("formatblock",   tg);
47253         this.undoManager.addEvent(); 
47254     },
47255     
47256     insertText : function(txt)
47257     {
47258         
47259         
47260         var range = this.createRange();
47261         range.deleteContents();
47262                //alert(Sender.getAttribute('label'));
47263                
47264         range.insertNode(this.doc.createTextNode(txt));
47265         this.undoManager.addEvent();
47266     } ,
47267     
47268      
47269
47270     /**
47271      * Executes a Midas editor command on the editor document and performs necessary focus and
47272      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
47273      * @param {String} cmd The Midas command
47274      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47275      */
47276     relayCmd : function(cmd, value){
47277         this.win.focus();
47278         this.execCmd(cmd, value);
47279         this.owner.fireEvent('editorevent', this);
47280         //this.updateToolbar();
47281         this.owner.deferFocus();
47282     },
47283
47284     /**
47285      * Executes a Midas editor command directly on the editor document.
47286      * For visual commands, you should use {@link #relayCmd} instead.
47287      * <b>This should only be called after the editor is initialized.</b>
47288      * @param {String} cmd The Midas command
47289      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
47290      */
47291     execCmd : function(cmd, value){
47292         this.doc.execCommand(cmd, false, value === undefined ? null : value);
47293         this.syncValue();
47294     },
47295  
47296  
47297    
47298     /**
47299      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
47300      * to insert tRoo.
47301      * @param {String} text | dom node.. 
47302      */
47303     insertAtCursor : function(text)
47304     {
47305         
47306         if(!this.activated){
47307             return;
47308         }
47309          
47310         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
47311             this.win.focus();
47312             
47313             
47314             // from jquery ui (MIT licenced)
47315             var range, node;
47316             var win = this.win;
47317             
47318             if (win.getSelection && win.getSelection().getRangeAt) {
47319                 
47320                 // delete the existing?
47321                 
47322                 this.createRange(this.getSelection()).deleteContents();
47323                 range = win.getSelection().getRangeAt(0);
47324                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
47325                 range.insertNode(node);
47326                 range = range.cloneRange();
47327                 range.collapse(false);
47328                  
47329                 win.getSelection().removeAllRanges();
47330                 win.getSelection().addRange(range);
47331                 
47332                 
47333                 
47334             } else if (win.document.selection && win.document.selection.createRange) {
47335                 // no firefox support
47336                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47337                 win.document.selection.createRange().pasteHTML(txt);
47338             
47339             } else {
47340                 // no firefox support
47341                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
47342                 this.execCmd('InsertHTML', txt);
47343             } 
47344             this.syncValue();
47345             
47346             this.deferFocus();
47347         }
47348     },
47349  // private
47350     mozKeyPress : function(e){
47351         if(e.ctrlKey){
47352             var c = e.getCharCode(), cmd;
47353           
47354             if(c > 0){
47355                 c = String.fromCharCode(c).toLowerCase();
47356                 switch(c){
47357                     case 'b':
47358                         cmd = 'bold';
47359                         break;
47360                     case 'i':
47361                         cmd = 'italic';
47362                         break;
47363                     
47364                     case 'u':
47365                         cmd = 'underline';
47366                         break;
47367                     
47368                     //case 'v':
47369                       //  this.cleanUpPaste.defer(100, this);
47370                       //  return;
47371                         
47372                 }
47373                 if(cmd){
47374                     this.win.focus();
47375                     this.execCmd(cmd);
47376                     this.deferFocus();
47377                     e.preventDefault();
47378                 }
47379                 
47380             }
47381         }
47382     },
47383
47384     // private
47385     fixKeys : function(){ // load time branching for fastest keydown performance
47386         if(Roo.isIE){
47387             return function(e){
47388                 var k = e.getKey(), r;
47389                 if(k == e.TAB){
47390                     e.stopEvent();
47391                     r = this.doc.selection.createRange();
47392                     if(r){
47393                         r.collapse(true);
47394                         r.pasteHTML('&#160;&#160;&#160;&#160;');
47395                         this.deferFocus();
47396                     }
47397                     return;
47398                 }
47399                 
47400                 if(k == e.ENTER){
47401                     r = this.doc.selection.createRange();
47402                     if(r){
47403                         var target = r.parentElement();
47404                         if(!target || target.tagName.toLowerCase() != 'li'){
47405                             e.stopEvent();
47406                             r.pasteHTML('<br/>');
47407                             r.collapse(false);
47408                             r.select();
47409                         }
47410                     }
47411                 }
47412                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47413                 //    this.cleanUpPaste.defer(100, this);
47414                 //    return;
47415                 //}
47416                 
47417                 
47418             };
47419         }else if(Roo.isOpera){
47420             return function(e){
47421                 var k = e.getKey();
47422                 if(k == e.TAB){
47423                     e.stopEvent();
47424                     this.win.focus();
47425                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47426                     this.deferFocus();
47427                 }
47428                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47429                 //    this.cleanUpPaste.defer(100, this);
47430                  //   return;
47431                 //}
47432                 
47433             };
47434         }else if(Roo.isSafari){
47435             return function(e){
47436                 var k = e.getKey();
47437                 
47438                 if(k == e.TAB){
47439                     e.stopEvent();
47440                     this.execCmd('InsertText','\t');
47441                     this.deferFocus();
47442                     return;
47443                 }
47444                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47445                  //   this.cleanUpPaste.defer(100, this);
47446                  //   return;
47447                // }
47448                 
47449              };
47450         }
47451     }(),
47452     
47453     getAllAncestors: function()
47454     {
47455         var p = this.getSelectedNode();
47456         var a = [];
47457         if (!p) {
47458             a.push(p); // push blank onto stack..
47459             p = this.getParentElement();
47460         }
47461         
47462         
47463         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47464             a.push(p);
47465             p = p.parentNode;
47466         }
47467         a.push(this.doc.body);
47468         return a;
47469     },
47470     lastSel : false,
47471     lastSelNode : false,
47472     
47473     
47474     getSelection : function() 
47475     {
47476         this.assignDocWin();
47477         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47478     },
47479     /**
47480      * Select a dom node
47481      * @param {DomElement} node the node to select
47482      */
47483     selectNode : function(node)
47484     {
47485         var nodeRange = node.ownerDocument.createRange();
47486         try {
47487             nodeRange.selectNode(node);
47488         } catch (e) {
47489             nodeRange.selectNodeContents(node);
47490         }
47491         //nodeRange.collapse(true);
47492         var s = this.win.getSelection();
47493         s.removeAllRanges();
47494         s.addRange(nodeRange);
47495     },
47496     
47497     getSelectedNode: function() 
47498     {
47499         // this may only work on Gecko!!!
47500         
47501         // should we cache this!!!!
47502         
47503         
47504         
47505          
47506         var range = this.createRange(this.getSelection()).cloneRange();
47507         
47508         if (Roo.isIE) {
47509             var parent = range.parentElement();
47510             while (true) {
47511                 var testRange = range.duplicate();
47512                 testRange.moveToElementText(parent);
47513                 if (testRange.inRange(range)) {
47514                     break;
47515                 }
47516                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47517                     break;
47518                 }
47519                 parent = parent.parentElement;
47520             }
47521             return parent;
47522         }
47523         
47524         // is ancestor a text element.
47525         var ac =  range.commonAncestorContainer;
47526         if (ac.nodeType == 3) {
47527             ac = ac.parentNode;
47528         }
47529         
47530         var ar = ac.childNodes;
47531          
47532         var nodes = [];
47533         var other_nodes = [];
47534         var has_other_nodes = false;
47535         for (var i=0;i<ar.length;i++) {
47536             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47537                 continue;
47538             }
47539             // fullly contained node.
47540             
47541             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47542                 nodes.push(ar[i]);
47543                 continue;
47544             }
47545             
47546             // probably selected..
47547             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47548                 other_nodes.push(ar[i]);
47549                 continue;
47550             }
47551             // outer..
47552             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47553                 continue;
47554             }
47555             
47556             
47557             has_other_nodes = true;
47558         }
47559         if (!nodes.length && other_nodes.length) {
47560             nodes= other_nodes;
47561         }
47562         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47563             return false;
47564         }
47565         
47566         return nodes[0];
47567     },
47568     createRange: function(sel)
47569     {
47570         // this has strange effects when using with 
47571         // top toolbar - not sure if it's a great idea.
47572         //this.editor.contentWindow.focus();
47573         if (typeof sel != "undefined") {
47574             try {
47575                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47576             } catch(e) {
47577                 return this.doc.createRange();
47578             }
47579         } else {
47580             return this.doc.createRange();
47581         }
47582     },
47583     getParentElement: function()
47584     {
47585         
47586         this.assignDocWin();
47587         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47588         
47589         var range = this.createRange(sel);
47590          
47591         try {
47592             var p = range.commonAncestorContainer;
47593             while (p.nodeType == 3) { // text node
47594                 p = p.parentNode;
47595             }
47596             return p;
47597         } catch (e) {
47598             return null;
47599         }
47600     
47601     },
47602     /***
47603      *
47604      * Range intersection.. the hard stuff...
47605      *  '-1' = before
47606      *  '0' = hits..
47607      *  '1' = after.
47608      *         [ -- selected range --- ]
47609      *   [fail]                        [fail]
47610      *
47611      *    basically..
47612      *      if end is before start or  hits it. fail.
47613      *      if start is after end or hits it fail.
47614      *
47615      *   if either hits (but other is outside. - then it's not 
47616      *   
47617      *    
47618      **/
47619     
47620     
47621     // @see http://www.thismuchiknow.co.uk/?p=64.
47622     rangeIntersectsNode : function(range, node)
47623     {
47624         var nodeRange = node.ownerDocument.createRange();
47625         try {
47626             nodeRange.selectNode(node);
47627         } catch (e) {
47628             nodeRange.selectNodeContents(node);
47629         }
47630     
47631         var rangeStartRange = range.cloneRange();
47632         rangeStartRange.collapse(true);
47633     
47634         var rangeEndRange = range.cloneRange();
47635         rangeEndRange.collapse(false);
47636     
47637         var nodeStartRange = nodeRange.cloneRange();
47638         nodeStartRange.collapse(true);
47639     
47640         var nodeEndRange = nodeRange.cloneRange();
47641         nodeEndRange.collapse(false);
47642     
47643         return rangeStartRange.compareBoundaryPoints(
47644                  Range.START_TO_START, nodeEndRange) == -1 &&
47645                rangeEndRange.compareBoundaryPoints(
47646                  Range.START_TO_START, nodeStartRange) == 1;
47647         
47648          
47649     },
47650     rangeCompareNode : function(range, node)
47651     {
47652         var nodeRange = node.ownerDocument.createRange();
47653         try {
47654             nodeRange.selectNode(node);
47655         } catch (e) {
47656             nodeRange.selectNodeContents(node);
47657         }
47658         
47659         
47660         range.collapse(true);
47661     
47662         nodeRange.collapse(true);
47663      
47664         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47665         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47666          
47667         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47668         
47669         var nodeIsBefore   =  ss == 1;
47670         var nodeIsAfter    = ee == -1;
47671         
47672         if (nodeIsBefore && nodeIsAfter) {
47673             return 0; // outer
47674         }
47675         if (!nodeIsBefore && nodeIsAfter) {
47676             return 1; //right trailed.
47677         }
47678         
47679         if (nodeIsBefore && !nodeIsAfter) {
47680             return 2;  // left trailed.
47681         }
47682         // fully contined.
47683         return 3;
47684     },
47685  
47686     cleanWordChars : function(input) {// change the chars to hex code
47687         
47688        var swapCodes  = [ 
47689             [    8211, "&#8211;" ], 
47690             [    8212, "&#8212;" ], 
47691             [    8216,  "'" ],  
47692             [    8217, "'" ],  
47693             [    8220, '"' ],  
47694             [    8221, '"' ],  
47695             [    8226, "*" ],  
47696             [    8230, "..." ]
47697         ]; 
47698         var output = input;
47699         Roo.each(swapCodes, function(sw) { 
47700             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47701             
47702             output = output.replace(swapper, sw[1]);
47703         });
47704         
47705         return output;
47706     },
47707     
47708      
47709     
47710         
47711     
47712     cleanUpChild : function (node)
47713     {
47714         
47715         new Roo.htmleditor.FilterComment({node : node});
47716         new Roo.htmleditor.FilterAttributes({
47717                 node : node,
47718                 attrib_black : this.ablack,
47719                 attrib_clean : this.aclean,
47720                 style_white : this.cwhite,
47721                 style_black : this.cblack
47722         });
47723         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47724         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47725          
47726         
47727     },
47728     
47729     /**
47730      * Clean up MS wordisms...
47731      * @deprecated - use filter directly
47732      */
47733     cleanWord : function(node)
47734     {
47735         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47736         
47737     },
47738    
47739     
47740     /**
47741
47742      * @deprecated - use filters
47743      */
47744     cleanTableWidths : function(node)
47745     {
47746         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47747         
47748  
47749     },
47750     
47751      
47752         
47753     applyBlacklists : function()
47754     {
47755         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47756         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47757         
47758         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47759         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47760         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47761         
47762         this.white = [];
47763         this.black = [];
47764         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47765             if (b.indexOf(tag) > -1) {
47766                 return;
47767             }
47768             this.white.push(tag);
47769             
47770         }, this);
47771         
47772         Roo.each(w, function(tag) {
47773             if (b.indexOf(tag) > -1) {
47774                 return;
47775             }
47776             if (this.white.indexOf(tag) > -1) {
47777                 return;
47778             }
47779             this.white.push(tag);
47780             
47781         }, this);
47782         
47783         
47784         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47785             if (w.indexOf(tag) > -1) {
47786                 return;
47787             }
47788             this.black.push(tag);
47789             
47790         }, this);
47791         
47792         Roo.each(b, function(tag) {
47793             if (w.indexOf(tag) > -1) {
47794                 return;
47795             }
47796             if (this.black.indexOf(tag) > -1) {
47797                 return;
47798             }
47799             this.black.push(tag);
47800             
47801         }, this);
47802         
47803         
47804         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47805         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47806         
47807         this.cwhite = [];
47808         this.cblack = [];
47809         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47810             if (b.indexOf(tag) > -1) {
47811                 return;
47812             }
47813             this.cwhite.push(tag);
47814             
47815         }, this);
47816         
47817         Roo.each(w, function(tag) {
47818             if (b.indexOf(tag) > -1) {
47819                 return;
47820             }
47821             if (this.cwhite.indexOf(tag) > -1) {
47822                 return;
47823             }
47824             this.cwhite.push(tag);
47825             
47826         }, this);
47827         
47828         
47829         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47830             if (w.indexOf(tag) > -1) {
47831                 return;
47832             }
47833             this.cblack.push(tag);
47834             
47835         }, this);
47836         
47837         Roo.each(b, function(tag) {
47838             if (w.indexOf(tag) > -1) {
47839                 return;
47840             }
47841             if (this.cblack.indexOf(tag) > -1) {
47842                 return;
47843             }
47844             this.cblack.push(tag);
47845             
47846         }, this);
47847     },
47848     
47849     setStylesheets : function(stylesheets)
47850     {
47851         if(typeof(stylesheets) == 'string'){
47852             Roo.get(this.iframe.contentDocument.head).createChild({
47853                 tag : 'link',
47854                 rel : 'stylesheet',
47855                 type : 'text/css',
47856                 href : stylesheets
47857             });
47858             
47859             return;
47860         }
47861         var _this = this;
47862      
47863         Roo.each(stylesheets, function(s) {
47864             if(!s.length){
47865                 return;
47866             }
47867             
47868             Roo.get(_this.iframe.contentDocument.head).createChild({
47869                 tag : 'link',
47870                 rel : 'stylesheet',
47871                 type : 'text/css',
47872                 href : s
47873             });
47874         });
47875
47876         
47877     },
47878     
47879     removeStylesheets : function()
47880     {
47881         var _this = this;
47882         
47883         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47884             s.remove();
47885         });
47886     },
47887     
47888     setStyle : function(style)
47889     {
47890         Roo.get(this.iframe.contentDocument.head).createChild({
47891             tag : 'style',
47892             type : 'text/css',
47893             html : style
47894         });
47895
47896         return;
47897     }
47898     
47899     // hide stuff that is not compatible
47900     /**
47901      * @event blur
47902      * @hide
47903      */
47904     /**
47905      * @event change
47906      * @hide
47907      */
47908     /**
47909      * @event focus
47910      * @hide
47911      */
47912     /**
47913      * @event specialkey
47914      * @hide
47915      */
47916     /**
47917      * @cfg {String} fieldClass @hide
47918      */
47919     /**
47920      * @cfg {String} focusClass @hide
47921      */
47922     /**
47923      * @cfg {String} autoCreate @hide
47924      */
47925     /**
47926      * @cfg {String} inputType @hide
47927      */
47928     /**
47929      * @cfg {String} invalidClass @hide
47930      */
47931     /**
47932      * @cfg {String} invalidText @hide
47933      */
47934     /**
47935      * @cfg {String} msgFx @hide
47936      */
47937     /**
47938      * @cfg {String} validateOnBlur @hide
47939      */
47940 });
47941
47942 Roo.HtmlEditorCore.white = [
47943         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47944         
47945        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47946        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47947        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47948        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47949        'TABLE',   'UL',         'XMP', 
47950        
47951        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47952       'THEAD',   'TR', 
47953      
47954       'DIR', 'MENU', 'OL', 'UL', 'DL',
47955        
47956       'EMBED',  'OBJECT'
47957 ];
47958
47959
47960 Roo.HtmlEditorCore.black = [
47961     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47962         'APPLET', // 
47963         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47964         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47965         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47966         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47967         //'FONT' // CLEAN LATER..
47968         'COLGROUP', 'COL'  // messy tables.
47969         
47970 ];
47971 Roo.HtmlEditorCore.clean = [ // ?? needed???
47972      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47973 ];
47974 Roo.HtmlEditorCore.tag_remove = [
47975     'FONT', 'TBODY'  
47976 ];
47977 // attributes..
47978
47979 Roo.HtmlEditorCore.ablack = [
47980     'on'
47981 ];
47982     
47983 Roo.HtmlEditorCore.aclean = [ 
47984     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47985 ];
47986
47987 // protocols..
47988 Roo.HtmlEditorCore.pwhite= [
47989         'http',  'https',  'mailto'
47990 ];
47991
47992 // white listed style attributes.
47993 Roo.HtmlEditorCore.cwhite= [
47994       //  'text-align', /// default is to allow most things..
47995       
47996          
47997 //        'font-size'//??
47998 ];
47999
48000 // black listed style attributes.
48001 Roo.HtmlEditorCore.cblack= [
48002       //  'font-size' -- this can be set by the project 
48003 ];
48004
48005
48006
48007
48008     //<script type="text/javascript">
48009
48010 /*
48011  * Ext JS Library 1.1.1
48012  * Copyright(c) 2006-2007, Ext JS, LLC.
48013  * Licence LGPL
48014  * 
48015  */
48016  
48017  
48018 Roo.form.HtmlEditor = function(config){
48019     
48020     
48021     
48022     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
48023     
48024     if (!this.toolbars) {
48025         this.toolbars = [];
48026     }
48027     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
48028     
48029     
48030 };
48031
48032 /**
48033  * @class Roo.form.HtmlEditor
48034  * @extends Roo.form.Field
48035  * Provides a lightweight HTML Editor component.
48036  *
48037  * This has been tested on Fireforx / Chrome.. IE may not be so great..
48038  * 
48039  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
48040  * supported by this editor.</b><br/><br/>
48041  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
48042  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
48043  */
48044 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
48045     /**
48046      * @cfg {Boolean} clearUp
48047      */
48048     clearUp : true,
48049       /**
48050      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
48051      */
48052     toolbars : false,
48053    
48054      /**
48055      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
48056      *                        Roo.resizable.
48057      */
48058     resizable : false,
48059      /**
48060      * @cfg {Number} height (in pixels)
48061      */   
48062     height: 300,
48063    /**
48064      * @cfg {Number} width (in pixels)
48065      */   
48066     width: 500,
48067     
48068     /**
48069      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
48070      * 
48071      */
48072     stylesheets: false,
48073     
48074     
48075      /**
48076      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
48077      * 
48078      */
48079     cblack: false,
48080     /**
48081      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
48082      * 
48083      */
48084     cwhite: false,
48085     
48086      /**
48087      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
48088      * 
48089      */
48090     black: false,
48091     /**
48092      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
48093      * 
48094      */
48095     white: false,
48096     /**
48097      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
48098      */
48099     allowComments: false,
48100     /**
48101      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
48102      */
48103     
48104     
48105      bodyCls : '',
48106     
48107     // id of frame..
48108     frameId: false,
48109     
48110     // private properties
48111     validationEvent : false,
48112     deferHeight: true,
48113     initialized : false,
48114     activated : false,
48115     
48116     onFocus : Roo.emptyFn,
48117     iframePad:3,
48118     hideMode:'offsets',
48119     
48120     actionMode : 'container', // defaults to hiding it...
48121     
48122     defaultAutoCreate : { // modified by initCompnoent..
48123         tag: "textarea",
48124         style:"width:500px;height:300px;",
48125         autocomplete: "new-password"
48126     },
48127
48128     // private
48129     initComponent : function(){
48130         this.addEvents({
48131             /**
48132              * @event initialize
48133              * Fires when the editor is fully initialized (including the iframe)
48134              * @param {HtmlEditor} this
48135              */
48136             initialize: true,
48137             /**
48138              * @event activate
48139              * Fires when the editor is first receives the focus. Any insertion must wait
48140              * until after this event.
48141              * @param {HtmlEditor} this
48142              */
48143             activate: true,
48144              /**
48145              * @event beforesync
48146              * Fires before the textarea is updated with content from the editor iframe. Return false
48147              * to cancel the sync.
48148              * @param {HtmlEditor} this
48149              * @param {String} html
48150              */
48151             beforesync: true,
48152              /**
48153              * @event beforepush
48154              * Fires before the iframe editor is updated with content from the textarea. Return false
48155              * to cancel the push.
48156              * @param {HtmlEditor} this
48157              * @param {String} html
48158              */
48159             beforepush: true,
48160              /**
48161              * @event sync
48162              * Fires when the textarea is updated with content from the editor iframe.
48163              * @param {HtmlEditor} this
48164              * @param {String} html
48165              */
48166             sync: true,
48167              /**
48168              * @event push
48169              * Fires when the iframe editor is updated with content from the textarea.
48170              * @param {HtmlEditor} this
48171              * @param {String} html
48172              */
48173             push: true,
48174              /**
48175              * @event editmodechange
48176              * Fires when the editor switches edit modes
48177              * @param {HtmlEditor} this
48178              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
48179              */
48180             editmodechange: true,
48181             /**
48182              * @event editorevent
48183              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
48184              * @param {HtmlEditor} this
48185              */
48186             editorevent: true,
48187             /**
48188              * @event firstfocus
48189              * Fires when on first focus - needed by toolbars..
48190              * @param {HtmlEditor} this
48191              */
48192             firstfocus: true,
48193             /**
48194              * @event autosave
48195              * Auto save the htmlEditor value as a file into Events
48196              * @param {HtmlEditor} this
48197              */
48198             autosave: true,
48199             /**
48200              * @event savedpreview
48201              * preview the saved version of htmlEditor
48202              * @param {HtmlEditor} this
48203              */
48204             savedpreview: true,
48205             
48206             /**
48207             * @event stylesheetsclick
48208             * Fires when press the Sytlesheets button
48209             * @param {Roo.HtmlEditorCore} this
48210             */
48211             stylesheetsclick: true,
48212             /**
48213             * @event paste
48214             * Fires when press user pastes into the editor
48215             * @param {Roo.HtmlEditorCore} this
48216             */
48217             paste: true 
48218         });
48219         this.defaultAutoCreate =  {
48220             tag: "textarea",
48221             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
48222             autocomplete: "new-password"
48223         };
48224     },
48225
48226     /**
48227      * Protected method that will not generally be called directly. It
48228      * is called when the editor creates its toolbar. Override this method if you need to
48229      * add custom toolbar buttons.
48230      * @param {HtmlEditor} editor
48231      */
48232     createToolbar : function(editor){
48233         Roo.log("create toolbars");
48234         if (!editor.toolbars || !editor.toolbars.length) {
48235             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
48236         }
48237         
48238         for (var i =0 ; i < editor.toolbars.length;i++) {
48239             editor.toolbars[i] = Roo.factory(
48240                     typeof(editor.toolbars[i]) == 'string' ?
48241                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
48242                 Roo.form.HtmlEditor);
48243             editor.toolbars[i].init(editor);
48244         }
48245          
48246         
48247     },
48248
48249      
48250     // private
48251     onRender : function(ct, position)
48252     {
48253         var _t = this;
48254         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
48255         
48256         this.wrap = this.el.wrap({
48257             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
48258         });
48259         
48260         this.editorcore.onRender(ct, position);
48261          
48262         if (this.resizable) {
48263             this.resizeEl = new Roo.Resizable(this.wrap, {
48264                 pinned : true,
48265                 wrap: true,
48266                 dynamic : true,
48267                 minHeight : this.height,
48268                 height: this.height,
48269                 handles : this.resizable,
48270                 width: this.width,
48271                 listeners : {
48272                     resize : function(r, w, h) {
48273                         _t.onResize(w,h); // -something
48274                     }
48275                 }
48276             });
48277             
48278         }
48279         this.createToolbar(this);
48280        
48281         
48282         if(!this.width){
48283             this.setSize(this.wrap.getSize());
48284         }
48285         if (this.resizeEl) {
48286             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
48287             // should trigger onReize..
48288         }
48289         
48290         this.keyNav = new Roo.KeyNav(this.el, {
48291             
48292             "tab" : function(e){
48293                 e.preventDefault();
48294                 
48295                 var value = this.getValue();
48296                 
48297                 var start = this.el.dom.selectionStart;
48298                 var end = this.el.dom.selectionEnd;
48299                 
48300                 if(!e.shiftKey){
48301                     
48302                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
48303                     this.el.dom.setSelectionRange(end + 1, end + 1);
48304                     return;
48305                 }
48306                 
48307                 var f = value.substring(0, start).split("\t");
48308                 
48309                 if(f.pop().length != 0){
48310                     return;
48311                 }
48312                 
48313                 this.setValue(f.join("\t") + value.substring(end));
48314                 this.el.dom.setSelectionRange(start - 1, start - 1);
48315                 
48316             },
48317             
48318             "home" : function(e){
48319                 e.preventDefault();
48320                 
48321                 var curr = this.el.dom.selectionStart;
48322                 var lines = this.getValue().split("\n");
48323                 
48324                 if(!lines.length){
48325                     return;
48326                 }
48327                 
48328                 if(e.ctrlKey){
48329                     this.el.dom.setSelectionRange(0, 0);
48330                     return;
48331                 }
48332                 
48333                 var pos = 0;
48334                 
48335                 for (var i = 0; i < lines.length;i++) {
48336                     pos += lines[i].length;
48337                     
48338                     if(i != 0){
48339                         pos += 1;
48340                     }
48341                     
48342                     if(pos < curr){
48343                         continue;
48344                     }
48345                     
48346                     pos -= lines[i].length;
48347                     
48348                     break;
48349                 }
48350                 
48351                 if(!e.shiftKey){
48352                     this.el.dom.setSelectionRange(pos, pos);
48353                     return;
48354                 }
48355                 
48356                 this.el.dom.selectionStart = pos;
48357                 this.el.dom.selectionEnd = curr;
48358             },
48359             
48360             "end" : function(e){
48361                 e.preventDefault();
48362                 
48363                 var curr = this.el.dom.selectionStart;
48364                 var lines = this.getValue().split("\n");
48365                 
48366                 if(!lines.length){
48367                     return;
48368                 }
48369                 
48370                 if(e.ctrlKey){
48371                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
48372                     return;
48373                 }
48374                 
48375                 var pos = 0;
48376                 
48377                 for (var i = 0; i < lines.length;i++) {
48378                     
48379                     pos += lines[i].length;
48380                     
48381                     if(i != 0){
48382                         pos += 1;
48383                     }
48384                     
48385                     if(pos < curr){
48386                         continue;
48387                     }
48388                     
48389                     break;
48390                 }
48391                 
48392                 if(!e.shiftKey){
48393                     this.el.dom.setSelectionRange(pos, pos);
48394                     return;
48395                 }
48396                 
48397                 this.el.dom.selectionStart = curr;
48398                 this.el.dom.selectionEnd = pos;
48399             },
48400
48401             scope : this,
48402
48403             doRelay : function(foo, bar, hname){
48404                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48405             },
48406
48407             forceKeyDown: true
48408         });
48409         
48410 //        if(this.autosave && this.w){
48411 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48412 //        }
48413     },
48414
48415     // private
48416     onResize : function(w, h)
48417     {
48418         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48419         var ew = false;
48420         var eh = false;
48421         
48422         if(this.el ){
48423             if(typeof w == 'number'){
48424                 var aw = w - this.wrap.getFrameWidth('lr');
48425                 this.el.setWidth(this.adjustWidth('textarea', aw));
48426                 ew = aw;
48427             }
48428             if(typeof h == 'number'){
48429                 var tbh = 0;
48430                 for (var i =0; i < this.toolbars.length;i++) {
48431                     // fixme - ask toolbars for heights?
48432                     tbh += this.toolbars[i].tb.el.getHeight();
48433                     if (this.toolbars[i].footer) {
48434                         tbh += this.toolbars[i].footer.el.getHeight();
48435                     }
48436                 }
48437                 
48438                 
48439                 
48440                 
48441                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48442                 ah -= 5; // knock a few pixes off for look..
48443 //                Roo.log(ah);
48444                 this.el.setHeight(this.adjustWidth('textarea', ah));
48445                 var eh = ah;
48446             }
48447         }
48448         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48449         this.editorcore.onResize(ew,eh);
48450         
48451     },
48452
48453     /**
48454      * Toggles the editor between standard and source edit mode.
48455      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48456      */
48457     toggleSourceEdit : function(sourceEditMode)
48458     {
48459         this.editorcore.toggleSourceEdit(sourceEditMode);
48460         
48461         if(this.editorcore.sourceEditMode){
48462             Roo.log('editor - showing textarea');
48463             
48464 //            Roo.log('in');
48465 //            Roo.log(this.syncValue());
48466             this.editorcore.syncValue();
48467             this.el.removeClass('x-hidden');
48468             this.el.dom.removeAttribute('tabIndex');
48469             this.el.focus();
48470             this.el.dom.scrollTop = 0;
48471             
48472             
48473             for (var i = 0; i < this.toolbars.length; i++) {
48474                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48475                     this.toolbars[i].tb.hide();
48476                     this.toolbars[i].footer.hide();
48477                 }
48478             }
48479             
48480         }else{
48481             Roo.log('editor - hiding textarea');
48482 //            Roo.log('out')
48483 //            Roo.log(this.pushValue()); 
48484             this.editorcore.pushValue();
48485             
48486             this.el.addClass('x-hidden');
48487             this.el.dom.setAttribute('tabIndex', -1);
48488             
48489             for (var i = 0; i < this.toolbars.length; i++) {
48490                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48491                     this.toolbars[i].tb.show();
48492                     this.toolbars[i].footer.show();
48493                 }
48494             }
48495             
48496             //this.deferFocus();
48497         }
48498         
48499         this.setSize(this.wrap.getSize());
48500         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48501         
48502         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48503     },
48504  
48505     // private (for BoxComponent)
48506     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48507
48508     // private (for BoxComponent)
48509     getResizeEl : function(){
48510         return this.wrap;
48511     },
48512
48513     // private (for BoxComponent)
48514     getPositionEl : function(){
48515         return this.wrap;
48516     },
48517
48518     // private
48519     initEvents : function(){
48520         this.originalValue = this.getValue();
48521     },
48522
48523     /**
48524      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48525      * @method
48526      */
48527     markInvalid : Roo.emptyFn,
48528     /**
48529      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48530      * @method
48531      */
48532     clearInvalid : Roo.emptyFn,
48533
48534     setValue : function(v){
48535         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48536         this.editorcore.pushValue();
48537     },
48538
48539      
48540     // private
48541     deferFocus : function(){
48542         this.focus.defer(10, this);
48543     },
48544
48545     // doc'ed in Field
48546     focus : function(){
48547         this.editorcore.focus();
48548         
48549     },
48550       
48551
48552     // private
48553     onDestroy : function(){
48554         
48555         
48556         
48557         if(this.rendered){
48558             
48559             for (var i =0; i < this.toolbars.length;i++) {
48560                 // fixme - ask toolbars for heights?
48561                 this.toolbars[i].onDestroy();
48562             }
48563             
48564             this.wrap.dom.innerHTML = '';
48565             this.wrap.remove();
48566         }
48567     },
48568
48569     // private
48570     onFirstFocus : function(){
48571         //Roo.log("onFirstFocus");
48572         this.editorcore.onFirstFocus();
48573          for (var i =0; i < this.toolbars.length;i++) {
48574             this.toolbars[i].onFirstFocus();
48575         }
48576         
48577     },
48578     
48579     // private
48580     syncValue : function()
48581     {
48582         this.editorcore.syncValue();
48583     },
48584     
48585     pushValue : function()
48586     {
48587         this.editorcore.pushValue();
48588     },
48589     
48590     setStylesheets : function(stylesheets)
48591     {
48592         this.editorcore.setStylesheets(stylesheets);
48593     },
48594     
48595     removeStylesheets : function()
48596     {
48597         this.editorcore.removeStylesheets();
48598     }
48599      
48600     
48601     // hide stuff that is not compatible
48602     /**
48603      * @event blur
48604      * @hide
48605      */
48606     /**
48607      * @event change
48608      * @hide
48609      */
48610     /**
48611      * @event focus
48612      * @hide
48613      */
48614     /**
48615      * @event specialkey
48616      * @hide
48617      */
48618     /**
48619      * @cfg {String} fieldClass @hide
48620      */
48621     /**
48622      * @cfg {String} focusClass @hide
48623      */
48624     /**
48625      * @cfg {String} autoCreate @hide
48626      */
48627     /**
48628      * @cfg {String} inputType @hide
48629      */
48630     /**
48631      * @cfg {String} invalidClass @hide
48632      */
48633     /**
48634      * @cfg {String} invalidText @hide
48635      */
48636     /**
48637      * @cfg {String} msgFx @hide
48638      */
48639     /**
48640      * @cfg {String} validateOnBlur @hide
48641      */
48642 });
48643  
48644     // <script type="text/javascript">
48645 /*
48646  * Based on
48647  * Ext JS Library 1.1.1
48648  * Copyright(c) 2006-2007, Ext JS, LLC.
48649  *  
48650  
48651  */
48652
48653 /**
48654  * @class Roo.form.HtmlEditorToolbar1
48655  * Basic Toolbar
48656  * 
48657  * Usage:
48658  *
48659  new Roo.form.HtmlEditor({
48660     ....
48661     toolbars : [
48662         new Roo.form.HtmlEditorToolbar1({
48663             disable : { fonts: 1 , format: 1, ..., ... , ...],
48664             btns : [ .... ]
48665         })
48666     }
48667      
48668  * 
48669  * @cfg {Object} disable List of elements to disable..
48670  * @cfg {Array} btns List of additional buttons.
48671  * 
48672  * 
48673  * NEEDS Extra CSS? 
48674  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48675  */
48676  
48677 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48678 {
48679     
48680     Roo.apply(this, config);
48681     
48682     // default disabled, based on 'good practice'..
48683     this.disable = this.disable || {};
48684     Roo.applyIf(this.disable, {
48685         fontSize : true,
48686         colors : true,
48687         specialElements : true
48688     });
48689     
48690     
48691     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48692     // dont call parent... till later.
48693 }
48694
48695 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48696     
48697     tb: false,
48698     
48699     rendered: false,
48700     
48701     editor : false,
48702     editorcore : false,
48703     /**
48704      * @cfg {Object} disable  List of toolbar elements to disable
48705          
48706      */
48707     disable : false,
48708     
48709     
48710      /**
48711      * @cfg {String} createLinkText The default text for the create link prompt
48712      */
48713     createLinkText : 'Please enter the URL for the link:',
48714     /**
48715      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48716      */
48717     defaultLinkValue : 'http:/'+'/',
48718    
48719     
48720       /**
48721      * @cfg {Array} fontFamilies An array of available font families
48722      */
48723     fontFamilies : [
48724         'Arial',
48725         'Courier New',
48726         'Tahoma',
48727         'Times New Roman',
48728         'Verdana'
48729     ],
48730     
48731     specialChars : [
48732            "&#169;",
48733           "&#174;",     
48734           "&#8482;",    
48735           "&#163;" ,    
48736          // "&#8212;",    
48737           "&#8230;",    
48738           "&#247;" ,    
48739         //  "&#225;" ,     ?? a acute?
48740            "&#8364;"    , //Euro
48741        //   "&#8220;"    ,
48742         //  "&#8221;"    ,
48743         //  "&#8226;"    ,
48744           "&#176;"  //   , // degrees
48745
48746          // "&#233;"     , // e ecute
48747          // "&#250;"     , // u ecute?
48748     ],
48749     
48750     specialElements : [
48751         {
48752             text: "Insert Table",
48753             xtype: 'MenuItem',
48754             xns : Roo.Menu,
48755             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48756                 
48757         },
48758         {    
48759             text: "Insert Image",
48760             xtype: 'MenuItem',
48761             xns : Roo.Menu,
48762             ihtml : '<img src="about:blank"/>'
48763             
48764         }
48765         
48766          
48767     ],
48768     
48769     
48770     inputElements : [ 
48771             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48772             "input:submit", "input:button", "select", "textarea", "label" ],
48773     formats : [
48774         ["p"] ,  
48775         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48776         ["pre"],[ "code"], 
48777         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48778         ['div'],['span'],
48779         ['sup'],['sub']
48780     ],
48781     
48782     cleanStyles : [
48783         "font-size"
48784     ],
48785      /**
48786      * @cfg {String} defaultFont default font to use.
48787      */
48788     defaultFont: 'tahoma',
48789    
48790     fontSelect : false,
48791     
48792     
48793     formatCombo : false,
48794     
48795     init : function(editor)
48796     {
48797         this.editor = editor;
48798         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48799         var editorcore = this.editorcore;
48800         
48801         var _t = this;
48802         
48803         var fid = editorcore.frameId;
48804         var etb = this;
48805         function btn(id, toggle, handler){
48806             var xid = fid + '-'+ id ;
48807             return {
48808                 id : xid,
48809                 cmd : id,
48810                 cls : 'x-btn-icon x-edit-'+id,
48811                 enableToggle:toggle !== false,
48812                 scope: _t, // was editor...
48813                 handler:handler||_t.relayBtnCmd,
48814                 clickEvent:'mousedown',
48815                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48816                 tabIndex:-1
48817             };
48818         }
48819         
48820         
48821         
48822         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48823         this.tb = tb;
48824          // stop form submits
48825         tb.el.on('click', function(e){
48826             e.preventDefault(); // what does this do?
48827         });
48828
48829         if(!this.disable.font) { // && !Roo.isSafari){
48830             /* why no safari for fonts 
48831             editor.fontSelect = tb.el.createChild({
48832                 tag:'select',
48833                 tabIndex: -1,
48834                 cls:'x-font-select',
48835                 html: this.createFontOptions()
48836             });
48837             
48838             editor.fontSelect.on('change', function(){
48839                 var font = editor.fontSelect.dom.value;
48840                 editor.relayCmd('fontname', font);
48841                 editor.deferFocus();
48842             }, editor);
48843             
48844             tb.add(
48845                 editor.fontSelect.dom,
48846                 '-'
48847             );
48848             */
48849             
48850         };
48851         if(!this.disable.formats){
48852             this.formatCombo = new Roo.form.ComboBox({
48853                 store: new Roo.data.SimpleStore({
48854                     id : 'tag',
48855                     fields: ['tag'],
48856                     data : this.formats // from states.js
48857                 }),
48858                 blockFocus : true,
48859                 name : '',
48860                 //autoCreate : {tag: "div",  size: "20"},
48861                 displayField:'tag',
48862                 typeAhead: false,
48863                 mode: 'local',
48864                 editable : false,
48865                 triggerAction: 'all',
48866                 emptyText:'Add tag',
48867                 selectOnFocus:true,
48868                 width:135,
48869                 listeners : {
48870                     'select': function(c, r, i) {
48871                         editorcore.insertTag(r.get('tag'));
48872                         editor.focus();
48873                     }
48874                 }
48875
48876             });
48877             tb.addField(this.formatCombo);
48878             
48879         }
48880         
48881         if(!this.disable.format){
48882             tb.add(
48883                 btn('bold'),
48884                 btn('italic'),
48885                 btn('underline'),
48886                 btn('strikethrough')
48887             );
48888         };
48889         if(!this.disable.fontSize){
48890             tb.add(
48891                 '-',
48892                 
48893                 
48894                 btn('increasefontsize', false, editorcore.adjustFont),
48895                 btn('decreasefontsize', false, editorcore.adjustFont)
48896             );
48897         };
48898         
48899         
48900         if(!this.disable.colors){
48901             tb.add(
48902                 '-', {
48903                     id:editorcore.frameId +'-forecolor',
48904                     cls:'x-btn-icon x-edit-forecolor',
48905                     clickEvent:'mousedown',
48906                     tooltip: this.buttonTips['forecolor'] || undefined,
48907                     tabIndex:-1,
48908                     menu : new Roo.menu.ColorMenu({
48909                         allowReselect: true,
48910                         focus: Roo.emptyFn,
48911                         value:'000000',
48912                         plain:true,
48913                         selectHandler: function(cp, color){
48914                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48915                             editor.deferFocus();
48916                         },
48917                         scope: editorcore,
48918                         clickEvent:'mousedown'
48919                     })
48920                 }, {
48921                     id:editorcore.frameId +'backcolor',
48922                     cls:'x-btn-icon x-edit-backcolor',
48923                     clickEvent:'mousedown',
48924                     tooltip: this.buttonTips['backcolor'] || undefined,
48925                     tabIndex:-1,
48926                     menu : new Roo.menu.ColorMenu({
48927                         focus: Roo.emptyFn,
48928                         value:'FFFFFF',
48929                         plain:true,
48930                         allowReselect: true,
48931                         selectHandler: function(cp, color){
48932                             if(Roo.isGecko){
48933                                 editorcore.execCmd('useCSS', false);
48934                                 editorcore.execCmd('hilitecolor', color);
48935                                 editorcore.execCmd('useCSS', true);
48936                                 editor.deferFocus();
48937                             }else{
48938                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48939                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48940                                 editor.deferFocus();
48941                             }
48942                         },
48943                         scope:editorcore,
48944                         clickEvent:'mousedown'
48945                     })
48946                 }
48947             );
48948         };
48949         // now add all the items...
48950         
48951
48952         if(!this.disable.alignments){
48953             tb.add(
48954                 '-',
48955                 btn('justifyleft'),
48956                 btn('justifycenter'),
48957                 btn('justifyright')
48958             );
48959         };
48960
48961         //if(!Roo.isSafari){
48962             if(!this.disable.links){
48963                 tb.add(
48964                     '-',
48965                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48966                 );
48967             };
48968
48969             if(!this.disable.lists){
48970                 tb.add(
48971                     '-',
48972                     btn('insertorderedlist'),
48973                     btn('insertunorderedlist')
48974                 );
48975             }
48976             if(!this.disable.sourceEdit){
48977                 tb.add(
48978                     '-',
48979                     btn('sourceedit', true, function(btn){
48980                         this.toggleSourceEdit(btn.pressed);
48981                     })
48982                 );
48983             }
48984         //}
48985         
48986         var smenu = { };
48987         // special menu.. - needs to be tidied up..
48988         if (!this.disable.special) {
48989             smenu = {
48990                 text: "&#169;",
48991                 cls: 'x-edit-none',
48992                 
48993                 menu : {
48994                     items : []
48995                 }
48996             };
48997             for (var i =0; i < this.specialChars.length; i++) {
48998                 smenu.menu.items.push({
48999                     
49000                     html: this.specialChars[i],
49001                     handler: function(a,b) {
49002                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
49003                         //editor.insertAtCursor(a.html);
49004                         
49005                     },
49006                     tabIndex:-1
49007                 });
49008             }
49009             
49010             
49011             tb.add(smenu);
49012             
49013             
49014         }
49015         
49016         var cmenu = { };
49017         if (!this.disable.cleanStyles) {
49018             cmenu = {
49019                 cls: 'x-btn-icon x-btn-clear',
49020                 
49021                 menu : {
49022                     items : []
49023                 }
49024             };
49025             for (var i =0; i < this.cleanStyles.length; i++) {
49026                 cmenu.menu.items.push({
49027                     actiontype : this.cleanStyles[i],
49028                     html: 'Remove ' + this.cleanStyles[i],
49029                     handler: function(a,b) {
49030 //                        Roo.log(a);
49031 //                        Roo.log(b);
49032                         var c = Roo.get(editorcore.doc.body);
49033                         c.select('[style]').each(function(s) {
49034                             s.dom.style.removeProperty(a.actiontype);
49035                         });
49036                         editorcore.syncValue();
49037                     },
49038                     tabIndex:-1
49039                 });
49040             }
49041             cmenu.menu.items.push({
49042                 actiontype : 'tablewidths',
49043                 html: 'Remove Table Widths',
49044                 handler: function(a,b) {
49045                     editorcore.cleanTableWidths();
49046                     editorcore.syncValue();
49047                 },
49048                 tabIndex:-1
49049             });
49050             cmenu.menu.items.push({
49051                 actiontype : 'word',
49052                 html: 'Remove MS Word Formating',
49053                 handler: function(a,b) {
49054                     editorcore.cleanWord();
49055                     editorcore.syncValue();
49056                 },
49057                 tabIndex:-1
49058             });
49059             
49060             cmenu.menu.items.push({
49061                 actiontype : 'all',
49062                 html: 'Remove All Styles',
49063                 handler: function(a,b) {
49064                     
49065                     var c = Roo.get(editorcore.doc.body);
49066                     c.select('[style]').each(function(s) {
49067                         s.dom.removeAttribute('style');
49068                     });
49069                     editorcore.syncValue();
49070                 },
49071                 tabIndex:-1
49072             });
49073             
49074             cmenu.menu.items.push({
49075                 actiontype : 'all',
49076                 html: 'Remove All CSS Classes',
49077                 handler: function(a,b) {
49078                     
49079                     var c = Roo.get(editorcore.doc.body);
49080                     c.select('[class]').each(function(s) {
49081                         s.dom.removeAttribute('class');
49082                     });
49083                     editorcore.cleanWord();
49084                     editorcore.syncValue();
49085                 },
49086                 tabIndex:-1
49087             });
49088             
49089              cmenu.menu.items.push({
49090                 actiontype : 'tidy',
49091                 html: 'Tidy HTML Source',
49092                 handler: function(a,b) {
49093                     new Roo.htmleditor.Tidy(editorcore.doc.body);
49094                     editorcore.syncValue();
49095                 },
49096                 tabIndex:-1
49097             });
49098             
49099             
49100             tb.add(cmenu);
49101         }
49102          
49103         if (!this.disable.specialElements) {
49104             var semenu = {
49105                 text: "Other;",
49106                 cls: 'x-edit-none',
49107                 menu : {
49108                     items : []
49109                 }
49110             };
49111             for (var i =0; i < this.specialElements.length; i++) {
49112                 semenu.menu.items.push(
49113                     Roo.apply({ 
49114                         handler: function(a,b) {
49115                             editor.insertAtCursor(this.ihtml);
49116                         }
49117                     }, this.specialElements[i])
49118                 );
49119                     
49120             }
49121             
49122             tb.add(semenu);
49123             
49124             
49125         }
49126          
49127         
49128         if (this.btns) {
49129             for(var i =0; i< this.btns.length;i++) {
49130                 var b = Roo.factory(this.btns[i],this.btns[i].xns || Roo.form);
49131                 b.cls =  'x-edit-none';
49132                 
49133                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
49134                     b.cls += ' x-init-enable';
49135                 }
49136                 
49137                 b.scope = editorcore;
49138                 tb.add(b);
49139             }
49140         
49141         }
49142         
49143         
49144         
49145         // disable everything...
49146         
49147         this.tb.items.each(function(item){
49148             
49149            if(
49150                 item.id != editorcore.frameId+ '-sourceedit' && 
49151                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
49152             ){
49153                 
49154                 item.disable();
49155             }
49156         });
49157         this.rendered = true;
49158         
49159         // the all the btns;
49160         editor.on('editorevent', this.updateToolbar, this);
49161         // other toolbars need to implement this..
49162         //editor.on('editmodechange', this.updateToolbar, this);
49163     },
49164     
49165     
49166     relayBtnCmd : function(btn) {
49167         this.editorcore.relayCmd(btn.cmd);
49168     },
49169     // private used internally
49170     createLink : function(){
49171         Roo.log("create link?");
49172         var url = prompt(this.createLinkText, this.defaultLinkValue);
49173         if(url && url != 'http:/'+'/'){
49174             this.editorcore.relayCmd('createlink', url);
49175         }
49176     },
49177
49178     
49179     /**
49180      * Protected method that will not generally be called directly. It triggers
49181      * a toolbar update by reading the markup state of the current selection in the editor.
49182      */
49183     updateToolbar: function(){
49184
49185         if(!this.editorcore.activated){
49186             this.editor.onFirstFocus();
49187             return;
49188         }
49189
49190         var btns = this.tb.items.map, 
49191             doc = this.editorcore.doc,
49192             frameId = this.editorcore.frameId;
49193
49194         if(!this.disable.font && !Roo.isSafari){
49195             /*
49196             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
49197             if(name != this.fontSelect.dom.value){
49198                 this.fontSelect.dom.value = name;
49199             }
49200             */
49201         }
49202         if(!this.disable.format){
49203             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
49204             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
49205             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
49206             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
49207         }
49208         if(!this.disable.alignments){
49209             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
49210             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
49211             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
49212         }
49213         if(!Roo.isSafari && !this.disable.lists){
49214             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
49215             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
49216         }
49217         
49218         var ans = this.editorcore.getAllAncestors();
49219         if (this.formatCombo) {
49220             
49221             
49222             var store = this.formatCombo.store;
49223             this.formatCombo.setValue("");
49224             for (var i =0; i < ans.length;i++) {
49225                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
49226                     // select it..
49227                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
49228                     break;
49229                 }
49230             }
49231         }
49232         
49233         
49234         
49235         // hides menus... - so this cant be on a menu...
49236         Roo.menu.MenuMgr.hideAll();
49237
49238         //this.editorsyncValue();
49239     },
49240    
49241     
49242     createFontOptions : function(){
49243         var buf = [], fs = this.fontFamilies, ff, lc;
49244         
49245         
49246         
49247         for(var i = 0, len = fs.length; i< len; i++){
49248             ff = fs[i];
49249             lc = ff.toLowerCase();
49250             buf.push(
49251                 '<option value="',lc,'" style="font-family:',ff,';"',
49252                     (this.defaultFont == lc ? ' selected="true">' : '>'),
49253                     ff,
49254                 '</option>'
49255             );
49256         }
49257         return buf.join('');
49258     },
49259     
49260     toggleSourceEdit : function(sourceEditMode){
49261         
49262         Roo.log("toolbar toogle");
49263         if(sourceEditMode === undefined){
49264             sourceEditMode = !this.sourceEditMode;
49265         }
49266         this.sourceEditMode = sourceEditMode === true;
49267         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
49268         // just toggle the button?
49269         if(btn.pressed !== this.sourceEditMode){
49270             btn.toggle(this.sourceEditMode);
49271             return;
49272         }
49273         
49274         if(sourceEditMode){
49275             Roo.log("disabling buttons");
49276             this.tb.items.each(function(item){
49277                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
49278                     item.disable();
49279                 }
49280             });
49281           
49282         }else{
49283             Roo.log("enabling buttons");
49284             if(this.editorcore.initialized){
49285                 this.tb.items.each(function(item){
49286                     item.enable();
49287                 });
49288             }
49289             
49290         }
49291         Roo.log("calling toggole on editor");
49292         // tell the editor that it's been pressed..
49293         this.editor.toggleSourceEdit(sourceEditMode);
49294        
49295     },
49296      /**
49297      * Object collection of toolbar tooltips for the buttons in the editor. The key
49298      * is the command id associated with that button and the value is a valid QuickTips object.
49299      * For example:
49300 <pre><code>
49301 {
49302     bold : {
49303         title: 'Bold (Ctrl+B)',
49304         text: 'Make the selected text bold.',
49305         cls: 'x-html-editor-tip'
49306     },
49307     italic : {
49308         title: 'Italic (Ctrl+I)',
49309         text: 'Make the selected text italic.',
49310         cls: 'x-html-editor-tip'
49311     },
49312     ...
49313 </code></pre>
49314     * @type Object
49315      */
49316     buttonTips : {
49317         bold : {
49318             title: 'Bold (Ctrl+B)',
49319             text: 'Make the selected text bold.',
49320             cls: 'x-html-editor-tip'
49321         },
49322         italic : {
49323             title: 'Italic (Ctrl+I)',
49324             text: 'Make the selected text italic.',
49325             cls: 'x-html-editor-tip'
49326         },
49327         underline : {
49328             title: 'Underline (Ctrl+U)',
49329             text: 'Underline the selected text.',
49330             cls: 'x-html-editor-tip'
49331         },
49332         strikethrough : {
49333             title: 'Strikethrough',
49334             text: 'Strikethrough the selected text.',
49335             cls: 'x-html-editor-tip'
49336         },
49337         increasefontsize : {
49338             title: 'Grow Text',
49339             text: 'Increase the font size.',
49340             cls: 'x-html-editor-tip'
49341         },
49342         decreasefontsize : {
49343             title: 'Shrink Text',
49344             text: 'Decrease the font size.',
49345             cls: 'x-html-editor-tip'
49346         },
49347         backcolor : {
49348             title: 'Text Highlight Color',
49349             text: 'Change the background color of the selected text.',
49350             cls: 'x-html-editor-tip'
49351         },
49352         forecolor : {
49353             title: 'Font Color',
49354             text: 'Change the color of the selected text.',
49355             cls: 'x-html-editor-tip'
49356         },
49357         justifyleft : {
49358             title: 'Align Text Left',
49359             text: 'Align text to the left.',
49360             cls: 'x-html-editor-tip'
49361         },
49362         justifycenter : {
49363             title: 'Center Text',
49364             text: 'Center text in the editor.',
49365             cls: 'x-html-editor-tip'
49366         },
49367         justifyright : {
49368             title: 'Align Text Right',
49369             text: 'Align text to the right.',
49370             cls: 'x-html-editor-tip'
49371         },
49372         insertunorderedlist : {
49373             title: 'Bullet List',
49374             text: 'Start a bulleted list.',
49375             cls: 'x-html-editor-tip'
49376         },
49377         insertorderedlist : {
49378             title: 'Numbered List',
49379             text: 'Start a numbered list.',
49380             cls: 'x-html-editor-tip'
49381         },
49382         createlink : {
49383             title: 'Hyperlink',
49384             text: 'Make the selected text a hyperlink.',
49385             cls: 'x-html-editor-tip'
49386         },
49387         sourceedit : {
49388             title: 'Source Edit',
49389             text: 'Switch to source editing mode.',
49390             cls: 'x-html-editor-tip'
49391         }
49392     },
49393     // private
49394     onDestroy : function(){
49395         if(this.rendered){
49396             
49397             this.tb.items.each(function(item){
49398                 if(item.menu){
49399                     item.menu.removeAll();
49400                     if(item.menu.el){
49401                         item.menu.el.destroy();
49402                     }
49403                 }
49404                 item.destroy();
49405             });
49406              
49407         }
49408     },
49409     onFirstFocus: function() {
49410         this.tb.items.each(function(item){
49411            item.enable();
49412         });
49413     }
49414 });
49415
49416
49417
49418
49419 // <script type="text/javascript">
49420 /*
49421  * Based on
49422  * Ext JS Library 1.1.1
49423  * Copyright(c) 2006-2007, Ext JS, LLC.
49424  *  
49425  
49426  */
49427
49428  
49429 /**
49430  * @class Roo.form.HtmlEditor.ToolbarContext
49431  * Context Toolbar
49432  * 
49433  * Usage:
49434  *
49435  new Roo.form.HtmlEditor({
49436     ....
49437     toolbars : [
49438         { xtype: 'ToolbarStandard', styles : {} }
49439         { xtype: 'ToolbarContext', disable : {} }
49440     ]
49441 })
49442
49443      
49444  * 
49445  * @config : {Object} disable List of elements to disable.. (not done yet.)
49446  * @config : {Object} styles  Map of styles available.
49447  * 
49448  */
49449
49450 Roo.form.HtmlEditor.ToolbarContext = function(config)
49451 {
49452     
49453     Roo.apply(this, config);
49454     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49455     // dont call parent... till later.
49456     this.styles = this.styles || {};
49457 }
49458
49459  
49460
49461 Roo.form.HtmlEditor.ToolbarContext.types = {
49462     'IMG' : [
49463         {
49464             name : 'width',
49465             title: "Width",
49466             width: 40
49467         },
49468         {
49469             name : 'height',
49470             title: "Height",
49471             width: 40
49472         },
49473         {
49474             name : 'align',
49475             title: "Align",
49476             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49477             width : 80
49478             
49479         },
49480         {
49481             name : 'border',
49482             title: "Border",
49483             width: 40
49484         },
49485         {
49486             name : 'alt',
49487             title: "Alt",
49488             width: 120
49489         },
49490         {
49491             name : 'src',
49492             title: "Src",
49493             width: 220
49494         }
49495         
49496     ],
49497     
49498     'FIGURE' : [
49499         {
49500             name : 'align',
49501             title: "Align",
49502             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49503             width : 80  
49504         }
49505     ],
49506     'A' : [
49507         {
49508             name : 'name',
49509             title: "Name",
49510             width: 50
49511         },
49512         {
49513             name : 'target',
49514             title: "Target",
49515             width: 120
49516         },
49517         {
49518             name : 'href',
49519             title: "Href",
49520             width: 220
49521         } // border?
49522         
49523     ],
49524     
49525     'INPUT' : [
49526         {
49527             name : 'name',
49528             title: "name",
49529             width: 120
49530         },
49531         {
49532             name : 'value',
49533             title: "Value",
49534             width: 120
49535         },
49536         {
49537             name : 'width',
49538             title: "Width",
49539             width: 40
49540         }
49541     ],
49542     'LABEL' : [
49543          {
49544             name : 'for',
49545             title: "For",
49546             width: 120
49547         }
49548     ],
49549     'TEXTAREA' : [
49550         {
49551             name : 'name',
49552             title: "name",
49553             width: 120
49554         },
49555         {
49556             name : 'rows',
49557             title: "Rows",
49558             width: 20
49559         },
49560         {
49561             name : 'cols',
49562             title: "Cols",
49563             width: 20
49564         }
49565     ],
49566     'SELECT' : [
49567         {
49568             name : 'name',
49569             title: "name",
49570             width: 120
49571         },
49572         {
49573             name : 'selectoptions',
49574             title: "Options",
49575             width: 200
49576         }
49577     ],
49578     
49579     // should we really allow this??
49580     // should this just be 
49581     'BODY' : [
49582         
49583         {
49584             name : 'title',
49585             title: "Title",
49586             width: 200,
49587             disabled : true
49588         }
49589     ],
49590  
49591     '*' : [
49592         // empty.
49593     ]
49594
49595 };
49596
49597 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49598 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49599
49600 Roo.form.HtmlEditor.ToolbarContext.options = {
49601         'font-family'  : [ 
49602                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49603                 [ 'Courier New', 'Courier New'],
49604                 [ 'Tahoma', 'Tahoma'],
49605                 [ 'Times New Roman,serif', 'Times'],
49606                 [ 'Verdana','Verdana' ]
49607         ]
49608 };
49609
49610 // fixme - these need to be configurable..
49611  
49612
49613 //Roo.form.HtmlEditor.ToolbarContext.types
49614
49615
49616 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49617     
49618     tb: false,
49619     
49620     rendered: false,
49621     
49622     editor : false,
49623     editorcore : false,
49624     /**
49625      * @cfg {Object} disable  List of toolbar elements to disable
49626          
49627      */
49628     disable : false,
49629     /**
49630      * @cfg {Object} styles List of styles 
49631      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49632      *
49633      * These must be defined in the page, so they get rendered correctly..
49634      * .headline { }
49635      * TD.underline { }
49636      * 
49637      */
49638     styles : false,
49639     
49640     options: false,
49641     
49642     toolbars : false,
49643     
49644     init : function(editor)
49645     {
49646         this.editor = editor;
49647         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49648         var editorcore = this.editorcore;
49649         
49650         var fid = editorcore.frameId;
49651         var etb = this;
49652         function btn(id, toggle, handler){
49653             var xid = fid + '-'+ id ;
49654             return {
49655                 id : xid,
49656                 cmd : id,
49657                 cls : 'x-btn-icon x-edit-'+id,
49658                 enableToggle:toggle !== false,
49659                 scope: editorcore, // was editor...
49660                 handler:handler||editorcore.relayBtnCmd,
49661                 clickEvent:'mousedown',
49662                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49663                 tabIndex:-1
49664             };
49665         }
49666         // create a new element.
49667         var wdiv = editor.wrap.createChild({
49668                 tag: 'div'
49669             }, editor.wrap.dom.firstChild.nextSibling, true);
49670         
49671         // can we do this more than once??
49672         
49673          // stop form submits
49674       
49675  
49676         // disable everything...
49677         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49678         this.toolbars = {};
49679            
49680         for (var i in  ty) {
49681           
49682             this.toolbars[i] = this.buildToolbar(ty[i],i);
49683         }
49684         this.tb = this.toolbars.BODY;
49685         this.tb.el.show();
49686         this.buildFooter();
49687         this.footer.show();
49688         editor.on('hide', function( ) { this.footer.hide() }, this);
49689         editor.on('show', function( ) { this.footer.show() }, this);
49690         
49691          
49692         this.rendered = true;
49693         
49694         // the all the btns;
49695         editor.on('editorevent', this.updateToolbar, this);
49696         // other toolbars need to implement this..
49697         //editor.on('editmodechange', this.updateToolbar, this);
49698     },
49699     
49700     
49701     
49702     /**
49703      * Protected method that will not generally be called directly. It triggers
49704      * a toolbar update by reading the markup state of the current selection in the editor.
49705      *
49706      * Note you can force an update by calling on('editorevent', scope, false)
49707      */
49708     updateToolbar: function(editor ,ev, sel)
49709     {
49710         
49711         if (ev) {
49712             ev.stopEvent(); // se if we can stop this looping with mutiple events.
49713         }
49714         
49715         //Roo.log(ev);
49716         // capture mouse up - this is handy for selecting images..
49717         // perhaps should go somewhere else...
49718         if(!this.editorcore.activated){
49719              this.editor.onFirstFocus();
49720             return;
49721         }
49722         //Roo.log(ev ? ev.target : 'NOTARGET');
49723         
49724         
49725         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49726         // selectNode - might want to handle IE?
49727         
49728         
49729         
49730         if (ev &&
49731             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49732             ev.target && ev.target.tagName != 'BODY' ) { // && ev.target.tagName == 'IMG') {
49733             // they have click on an image...
49734             // let's see if we can change the selection...
49735             sel = ev.target;
49736             
49737             // this triggers looping?
49738             //this.editorcore.selectNode(sel);
49739              
49740         }  
49741         Roo.select('.roo-ed-selection', false, this.editorcore.doc).removeClass('roo-ed-selection');
49742         //Roo.get(node).addClass('roo-ed-selection');
49743       
49744         //var updateFooter = sel ? false : true; 
49745         
49746         
49747         var ans = this.editorcore.getAllAncestors();
49748         
49749         // pick
49750         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49751         
49752         if (!sel) { 
49753             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49754             sel = sel ? sel : this.editorcore.doc.body;
49755             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49756             
49757         }
49758         
49759         var tn = sel.tagName.toUpperCase();
49760         var lastSel = this.tb.selectedNode;
49761         this.tb.selectedNode = sel;
49762         var left_label = tn;
49763         
49764         // ok see if we are editing a block?
49765         
49766         var db = false;
49767         // you are not actually selecting the block.
49768         if (sel && sel.hasAttribute('data-block')) {
49769             db = sel;
49770         } else if (sel && !sel.hasAttribute('contenteditable')) {
49771             var sel_el = Roo.get(sel);
49772             db = sel_el.findParent('[data-block]');
49773             var cepar = sel_el.findParent('[contenteditable=true]');
49774             if (db && cepar && cepar.tagName != 'BODY') {
49775                db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49776             }   
49777         }
49778         
49779         
49780         var block = false;
49781         //if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49782         if (db) {
49783             block = Roo.htmleditor.Block.factory(db);
49784             
49785             
49786             if (block) {
49787                 db.className +=  ' roo-ed-selection'; // since we removed it earlier... its not there..
49788                 tn = 'BLOCK.' + db.getAttribute('data-block');
49789                 
49790                 //this.editorcore.selectNode(db);
49791                 if (typeof(this.toolbars[tn]) == 'undefined') {
49792                    this.toolbars[tn] = this.buildToolbar( false  ,tn ,block.friendly_name, block);
49793                 }
49794                 this.toolbars[tn].selectedNode = db;
49795                 left_label = block.friendly_name;
49796                 ans = this.editorcore.getAllAncestors();
49797             }
49798             
49799                 
49800             
49801         }
49802         
49803         
49804         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49805             return; // no change?
49806         }
49807         
49808         
49809           
49810         this.tb.el.hide();
49811         ///console.log("show: " + tn);
49812         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49813         
49814         this.tb.el.show();
49815         // update name
49816         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49817         
49818         
49819         // update attributes
49820         if (block && this.tb.fields) {
49821              
49822             this.tb.fields.each(function(e) {
49823                 e.setValue(block[e.name]);
49824             });
49825             
49826             
49827         } else  if (this.tb.fields && this.tb.selectedNode) {
49828             this.tb.fields.each( function(e) {
49829                 if (e.stylename) {
49830                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49831                     return;
49832                 } 
49833                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49834             }, this);
49835             this.updateToolbarStyles(this.tb.selectedNode);  
49836         }
49837         
49838         
49839        
49840         Roo.menu.MenuMgr.hideAll();
49841
49842         
49843         
49844     
49845         // update the footer
49846         //
49847         this.updateFooter(ans);
49848              
49849     },
49850     
49851     updateToolbarStyles : function(sel)
49852     {
49853         var hasStyles = false;
49854         for(var i in this.styles) {
49855             hasStyles = true;
49856             break;
49857         }
49858         
49859         // update styles
49860         if (hasStyles && this.tb.hasStyles) { 
49861             var st = this.tb.fields.item(0);
49862             
49863             st.store.removeAll();
49864             var cn = sel.className.split(/\s+/);
49865             
49866             var avs = [];
49867             if (this.styles['*']) {
49868                 
49869                 Roo.each(this.styles['*'], function(v) {
49870                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49871                 });
49872             }
49873             if (this.styles[tn]) { 
49874                 Roo.each(this.styles[tn], function(v) {
49875                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49876                 });
49877             }
49878             
49879             st.store.loadData(avs);
49880             st.collapse();
49881             st.setValue(cn);
49882         }
49883     },
49884     
49885      
49886     updateFooter : function(ans)
49887     {
49888         var html = '';
49889         if (ans === false) {
49890             this.footDisp.dom.innerHTML = '';
49891             return;
49892         }
49893         
49894         this.footerEls = ans.reverse();
49895         Roo.each(this.footerEls, function(a,i) {
49896             if (!a) { return; }
49897             html += html.length ? ' &gt; '  :  '';
49898             
49899             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49900             
49901         });
49902        
49903         // 
49904         var sz = this.footDisp.up('td').getSize();
49905         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49906         this.footDisp.dom.style.marginLeft = '5px';
49907         
49908         this.footDisp.dom.style.overflow = 'hidden';
49909         
49910         this.footDisp.dom.innerHTML = html;
49911             
49912         
49913     },
49914    
49915        
49916     // private
49917     onDestroy : function(){
49918         if(this.rendered){
49919             
49920             this.tb.items.each(function(item){
49921                 if(item.menu){
49922                     item.menu.removeAll();
49923                     if(item.menu.el){
49924                         item.menu.el.destroy();
49925                     }
49926                 }
49927                 item.destroy();
49928             });
49929              
49930         }
49931     },
49932     onFirstFocus: function() {
49933         // need to do this for all the toolbars..
49934         this.tb.items.each(function(item){
49935            item.enable();
49936         });
49937     },
49938     buildToolbar: function(tlist, nm, friendly_name, block)
49939     {
49940         var editor = this.editor;
49941         var editorcore = this.editorcore;
49942          // create a new element.
49943         var wdiv = editor.wrap.createChild({
49944                 tag: 'div'
49945             }, editor.wrap.dom.firstChild.nextSibling, true);
49946         
49947        
49948         var tb = new Roo.Toolbar(wdiv);
49949         ///this.tb = tb; // << this sets the active toolbar..
49950         if (tlist === false && block) {
49951             tlist = block.contextMenu(this);
49952         }
49953         
49954         tb.hasStyles = false;
49955         tb.name = nm;
49956         
49957         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49958         
49959         var styles = Array.from(this.styles);
49960         
49961         
49962         // styles...
49963         if (styles && styles.length) {
49964             tb.hasStyles = true;
49965             // this needs a multi-select checkbox...
49966             tb.addField( new Roo.form.ComboBox({
49967                 store: new Roo.data.SimpleStore({
49968                     id : 'val',
49969                     fields: ['val', 'selected'],
49970                     data : [] 
49971                 }),
49972                 name : '-roo-edit-className',
49973                 attrname : 'className',
49974                 displayField: 'val',
49975                 typeAhead: false,
49976                 mode: 'local',
49977                 editable : false,
49978                 triggerAction: 'all',
49979                 emptyText:'Select Style',
49980                 selectOnFocus:true,
49981                 width: 130,
49982                 listeners : {
49983                     'select': function(c, r, i) {
49984                         // initial support only for on class per el..
49985                         tb.selectedNode.className =  r ? r.get('val') : '';
49986                         editorcore.syncValue();
49987                     }
49988                 }
49989     
49990             }));
49991         }
49992         
49993         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49994         
49995         
49996         for (var i = 0; i < tlist.length; i++) {
49997             
49998             // newer versions will use xtype cfg to create menus.
49999             if (typeof(tlist[i].xtype) != 'undefined') {
50000                 
50001                 tb[typeof(tlist[i].name)== 'undefined' ? 'add' : 'addField'](Roo.factory(tlist[i]));
50002                 
50003                 
50004                 continue;
50005             }
50006             
50007             var item = tlist[i];
50008             tb.add(item.title + ":&nbsp;");
50009             
50010             
50011             //optname == used so you can configure the options available..
50012             var opts = item.opts ? item.opts : false;
50013             if (item.optname) { // use the b
50014                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
50015            
50016             }
50017             
50018             if (opts) {
50019                 // opts == pulldown..
50020                 tb.addField( new Roo.form.ComboBox({
50021                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
50022                         id : 'val',
50023                         fields: ['val', 'display'],
50024                         data : opts  
50025                     }),
50026                     name : '-roo-edit-' + tlist[i].name,
50027                     
50028                     attrname : tlist[i].name,
50029                     stylename : item.style ? item.style : false,
50030                     
50031                     displayField: item.displayField ? item.displayField : 'val',
50032                     valueField :  'val',
50033                     typeAhead: false,
50034                     mode: typeof(tbc.stores[tlist[i].name]) != 'undefined'  ? 'remote' : 'local',
50035                     editable : false,
50036                     triggerAction: 'all',
50037                     emptyText:'Select',
50038                     selectOnFocus:true,
50039                     width: item.width ? item.width  : 130,
50040                     listeners : {
50041                         'select': function(c, r, i) {
50042                             if (tb.selectedNode.hasAttribute('data-block')) {
50043                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50044                                 b[c.attrname] = r.get('val');
50045                                 b.updateElement(tb.selectedNode);
50046                                 editorcore.syncValue();
50047                                 return;
50048                             }
50049                             
50050                             if (c.stylename) {
50051                                 tb.selectedNode.style[c.stylename] =  r.get('val');
50052                                 editorcore.syncValue();
50053                                 return;
50054                             }
50055                             if (r === false) {
50056                                 tb.selectedNode.removeAttribute(c.attrname);
50057                                 editorcore.syncValue();
50058                                 return;
50059                             }
50060                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
50061                             editorcore.syncValue();
50062                         }
50063                     }
50064
50065                 }));
50066                 continue;
50067                     
50068                  
50069                 /*
50070                 tb.addField( new Roo.form.TextField({
50071                     name: i,
50072                     width: 100,
50073                     //allowBlank:false,
50074                     value: ''
50075                 }));
50076                 continue;
50077                 */
50078             }
50079             tb.addField( new Roo.form.TextField({
50080                 name: '-roo-edit-' + tlist[i].name,
50081                 attrname : tlist[i].name,
50082                 
50083                 width: item.width,
50084                 //allowBlank:true,
50085                 value: '',
50086                 listeners: {
50087                     'change' : function(f, nv, ov) {
50088                         
50089                         if (tb.selectedNode.hasAttribute('data-block')) {
50090                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
50091                             b[f.attrname] = nv;
50092                             b.updateElement(tb.selectedNode);
50093                             editorcore.syncValue();
50094                             return;
50095                         }
50096                         
50097                         tb.selectedNode.setAttribute(f.attrname, nv);
50098                         editorcore.syncValue();
50099                     }
50100                 }
50101             }));
50102              
50103         }
50104         
50105         var _this = this;
50106         
50107         if(nm == 'BODY'){
50108             tb.addSeparator();
50109         
50110             tb.addButton( {
50111                 text: 'Stylesheets',
50112
50113                 listeners : {
50114                     click : function ()
50115                     {
50116                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
50117                     }
50118                 }
50119             });
50120         }
50121         
50122         tb.addFill();
50123         tb.addButton({
50124             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
50125     
50126             listeners : {
50127                 click : function ()
50128                 {
50129                     var sn = tb.selectedNode;
50130                     if (block) {
50131                         sn = Roo.htmleditor.Block.factory(tb.selectedNode).removeNode();
50132                         
50133                     }
50134                     if (!sn) {
50135                         return;
50136                     }
50137                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
50138                     if (sn.hasAttribute('data-block')) {
50139                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
50140                         sn.parentNode.removeChild(sn);
50141                         
50142                     } else if (sn && sn.tagName != 'BODY') {
50143                         // remove and keep parents.
50144                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
50145                         a.removeTag(sn);
50146                     }
50147                     
50148                     
50149                     var range = editorcore.createRange();
50150         
50151                     range.setStart(stn,0);
50152                     range.setEnd(stn,0); 
50153                     var selection = editorcore.getSelection();
50154                     selection.removeAllRanges();
50155                     selection.addRange(range);
50156                     
50157                     
50158                     //_this.updateToolbar(null, null, pn);
50159                     _this.updateToolbar(null, null, null);
50160                     _this.updateFooter(false);
50161                     
50162                 }
50163             }
50164             
50165                     
50166                 
50167             
50168         });
50169         
50170         
50171         tb.el.on('click', function(e){
50172             e.preventDefault(); // what does this do?
50173         });
50174         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
50175         tb.el.hide();
50176         
50177         // dont need to disable them... as they will get hidden
50178         return tb;
50179          
50180         
50181     },
50182     buildFooter : function()
50183     {
50184         
50185         var fel = this.editor.wrap.createChild();
50186         this.footer = new Roo.Toolbar(fel);
50187         // toolbar has scrolly on left / right?
50188         var footDisp= new Roo.Toolbar.Fill();
50189         var _t = this;
50190         this.footer.add(
50191             {
50192                 text : '&lt;',
50193                 xtype: 'Button',
50194                 handler : function() {
50195                     _t.footDisp.scrollTo('left',0,true)
50196                 }
50197             }
50198         );
50199         this.footer.add( footDisp );
50200         this.footer.add( 
50201             {
50202                 text : '&gt;',
50203                 xtype: 'Button',
50204                 handler : function() {
50205                     // no animation..
50206                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
50207                 }
50208             }
50209         );
50210         var fel = Roo.get(footDisp.el);
50211         fel.addClass('x-editor-context');
50212         this.footDispWrap = fel; 
50213         this.footDispWrap.overflow  = 'hidden';
50214         
50215         this.footDisp = fel.createChild();
50216         this.footDispWrap.on('click', this.onContextClick, this)
50217         
50218         
50219     },
50220     // when the footer contect changes
50221     onContextClick : function (ev,dom)
50222     {
50223         ev.preventDefault();
50224         var  cn = dom.className;
50225         //Roo.log(cn);
50226         if (!cn.match(/x-ed-loc-/)) {
50227             return;
50228         }
50229         var n = cn.split('-').pop();
50230         var ans = this.footerEls;
50231         var sel = ans[n];
50232         
50233         this.editorcore.selectNode(sel);
50234         
50235         
50236         this.updateToolbar(null, null, sel);
50237         
50238         
50239     }
50240     
50241     
50242     
50243     
50244     
50245 });
50246
50247
50248
50249
50250
50251 /*
50252  * Based on:
50253  * Ext JS Library 1.1.1
50254  * Copyright(c) 2006-2007, Ext JS, LLC.
50255  *
50256  * Originally Released Under LGPL - original licence link has changed is not relivant.
50257  *
50258  * Fork - LGPL
50259  * <script type="text/javascript">
50260  */
50261  
50262 /**
50263  * @class Roo.form.BasicForm
50264  * @extends Roo.util.Observable
50265  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
50266  * @constructor
50267  * @param {String/HTMLElement/Roo.Element} el The form element or its id
50268  * @param {Object} config Configuration options
50269  */
50270 Roo.form.BasicForm = function(el, config){
50271     this.allItems = [];
50272     this.childForms = [];
50273     Roo.apply(this, config);
50274     /*
50275      * The Roo.form.Field items in this form.
50276      * @type MixedCollection
50277      */
50278      
50279      
50280     this.items = new Roo.util.MixedCollection(false, function(o){
50281         return o.id || (o.id = Roo.id());
50282     });
50283     this.addEvents({
50284         /**
50285          * @event beforeaction
50286          * Fires before any action is performed. Return false to cancel the action.
50287          * @param {Form} this
50288          * @param {Action} action The action to be performed
50289          */
50290         beforeaction: true,
50291         /**
50292          * @event actionfailed
50293          * Fires when an action fails.
50294          * @param {Form} this
50295          * @param {Action} action The action that failed
50296          */
50297         actionfailed : true,
50298         /**
50299          * @event actioncomplete
50300          * Fires when an action is completed.
50301          * @param {Form} this
50302          * @param {Action} action The action that completed
50303          */
50304         actioncomplete : true
50305     });
50306     if(el){
50307         this.initEl(el);
50308     }
50309     Roo.form.BasicForm.superclass.constructor.call(this);
50310     
50311     Roo.form.BasicForm.popover.apply();
50312 };
50313
50314 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
50315     /**
50316      * @cfg {String} method
50317      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
50318      */
50319     /**
50320      * @cfg {DataReader} reader
50321      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
50322      * This is optional as there is built-in support for processing JSON.
50323      */
50324     /**
50325      * @cfg {DataReader} errorReader
50326      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
50327      * This is completely optional as there is built-in support for processing JSON.
50328      */
50329     /**
50330      * @cfg {String} url
50331      * The URL to use for form actions if one isn't supplied in the action options.
50332      */
50333     /**
50334      * @cfg {Boolean} fileUpload
50335      * Set to true if this form is a file upload.
50336      */
50337      
50338     /**
50339      * @cfg {Object} baseParams
50340      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
50341      */
50342      /**
50343      
50344     /**
50345      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
50346      */
50347     timeout: 30,
50348
50349     // private
50350     activeAction : null,
50351
50352     /**
50353      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
50354      * or setValues() data instead of when the form was first created.
50355      */
50356     trackResetOnLoad : false,
50357     
50358     
50359     /**
50360      * childForms - used for multi-tab forms
50361      * @type {Array}
50362      */
50363     childForms : false,
50364     
50365     /**
50366      * allItems - full list of fields.
50367      * @type {Array}
50368      */
50369     allItems : false,
50370     
50371     /**
50372      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50373      * element by passing it or its id or mask the form itself by passing in true.
50374      * @type Mixed
50375      */
50376     waitMsgTarget : false,
50377     
50378     /**
50379      * @type Boolean
50380      */
50381     disableMask : false,
50382     
50383     /**
50384      * @cfg {Boolean} errorMask (true|false) default false
50385      */
50386     errorMask : false,
50387     
50388     /**
50389      * @cfg {Number} maskOffset Default 100
50390      */
50391     maskOffset : 100,
50392
50393     // private
50394     initEl : function(el){
50395         this.el = Roo.get(el);
50396         this.id = this.el.id || Roo.id();
50397         this.el.on('submit', this.onSubmit, this);
50398         this.el.addClass('x-form');
50399     },
50400
50401     // private
50402     onSubmit : function(e){
50403         e.stopEvent();
50404     },
50405
50406     /**
50407      * Returns true if client-side validation on the form is successful.
50408      * @return Boolean
50409      */
50410     isValid : function(){
50411         var valid = true;
50412         var target = false;
50413         this.items.each(function(f){
50414             if(f.validate()){
50415                 return;
50416             }
50417             
50418             valid = false;
50419                 
50420             if(!target && f.el.isVisible(true)){
50421                 target = f;
50422             }
50423         });
50424         
50425         if(this.errorMask && !valid){
50426             Roo.form.BasicForm.popover.mask(this, target);
50427         }
50428         
50429         return valid;
50430     },
50431     /**
50432      * Returns array of invalid form fields.
50433      * @return Array
50434      */
50435     
50436     invalidFields : function()
50437     {
50438         var ret = [];
50439         this.items.each(function(f){
50440             if(f.validate()){
50441                 return;
50442             }
50443             ret.push(f);
50444             
50445         });
50446         
50447         return ret;
50448     },
50449     
50450     
50451     /**
50452      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50453      * @return Boolean
50454      */
50455     isDirty : function(){
50456         var dirty = false;
50457         this.items.each(function(f){
50458            if(f.isDirty()){
50459                dirty = true;
50460                return false;
50461            }
50462         });
50463         return dirty;
50464     },
50465     
50466     /**
50467      * Returns true if any fields in this form have changed since their original load. (New version)
50468      * @return Boolean
50469      */
50470     
50471     hasChanged : function()
50472     {
50473         var dirty = false;
50474         this.items.each(function(f){
50475            if(f.hasChanged()){
50476                dirty = true;
50477                return false;
50478            }
50479         });
50480         return dirty;
50481         
50482     },
50483     /**
50484      * Resets all hasChanged to 'false' -
50485      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50486      * So hasChanged storage is only to be used for this purpose
50487      * @return Boolean
50488      */
50489     resetHasChanged : function()
50490     {
50491         this.items.each(function(f){
50492            f.resetHasChanged();
50493         });
50494         
50495     },
50496     
50497     
50498     /**
50499      * Performs a predefined action (submit or load) or custom actions you define on this form.
50500      * @param {String} actionName The name of the action type
50501      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50502      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50503      * accept other config options):
50504      * <pre>
50505 Property          Type             Description
50506 ----------------  ---------------  ----------------------------------------------------------------------------------
50507 url               String           The url for the action (defaults to the form's url)
50508 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50509 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50510 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50511                                    validate the form on the client (defaults to false)
50512      * </pre>
50513      * @return {BasicForm} this
50514      */
50515     doAction : function(action, options){
50516         if(typeof action == 'string'){
50517             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50518         }
50519         if(this.fireEvent('beforeaction', this, action) !== false){
50520             this.beforeAction(action);
50521             action.run.defer(100, action);
50522         }
50523         return this;
50524     },
50525
50526     /**
50527      * Shortcut to do a submit action.
50528      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50529      * @return {BasicForm} this
50530      */
50531     submit : function(options){
50532         this.doAction('submit', options);
50533         return this;
50534     },
50535
50536     /**
50537      * Shortcut to do a load action.
50538      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50539      * @return {BasicForm} this
50540      */
50541     load : function(options){
50542         this.doAction('load', options);
50543         return this;
50544     },
50545
50546     /**
50547      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50548      * @param {Record} record The record to edit
50549      * @return {BasicForm} this
50550      */
50551     updateRecord : function(record){
50552         record.beginEdit();
50553         var fs = record.fields;
50554         fs.each(function(f){
50555             var field = this.findField(f.name);
50556             if(field){
50557                 record.set(f.name, field.getValue());
50558             }
50559         }, this);
50560         record.endEdit();
50561         return this;
50562     },
50563
50564     /**
50565      * Loads an Roo.data.Record into this form.
50566      * @param {Record} record The record to load
50567      * @return {BasicForm} this
50568      */
50569     loadRecord : function(record){
50570         this.setValues(record.data);
50571         return this;
50572     },
50573
50574     // private
50575     beforeAction : function(action){
50576         var o = action.options;
50577         
50578         if(!this.disableMask) {
50579             if(this.waitMsgTarget === true){
50580                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50581             }else if(this.waitMsgTarget){
50582                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50583                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50584             }else {
50585                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50586             }
50587         }
50588         
50589          
50590     },
50591
50592     // private
50593     afterAction : function(action, success){
50594         this.activeAction = null;
50595         var o = action.options;
50596         
50597         if(!this.disableMask) {
50598             if(this.waitMsgTarget === true){
50599                 this.el.unmask();
50600             }else if(this.waitMsgTarget){
50601                 this.waitMsgTarget.unmask();
50602             }else{
50603                 Roo.MessageBox.updateProgress(1);
50604                 Roo.MessageBox.hide();
50605             }
50606         }
50607         
50608         if(success){
50609             if(o.reset){
50610                 this.reset();
50611             }
50612             Roo.callback(o.success, o.scope, [this, action]);
50613             this.fireEvent('actioncomplete', this, action);
50614             
50615         }else{
50616             
50617             // failure condition..
50618             // we have a scenario where updates need confirming.
50619             // eg. if a locking scenario exists..
50620             // we look for { errors : { needs_confirm : true }} in the response.
50621             if (
50622                 (typeof(action.result) != 'undefined')  &&
50623                 (typeof(action.result.errors) != 'undefined')  &&
50624                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50625            ){
50626                 var _t = this;
50627                 Roo.MessageBox.confirm(
50628                     "Change requires confirmation",
50629                     action.result.errorMsg,
50630                     function(r) {
50631                         if (r != 'yes') {
50632                             return;
50633                         }
50634                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50635                     }
50636                     
50637                 );
50638                 
50639                 
50640                 
50641                 return;
50642             }
50643             
50644             Roo.callback(o.failure, o.scope, [this, action]);
50645             // show an error message if no failed handler is set..
50646             if (!this.hasListener('actionfailed')) {
50647                 Roo.MessageBox.alert("Error",
50648                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50649                         action.result.errorMsg :
50650                         "Saving Failed, please check your entries or try again"
50651                 );
50652             }
50653             
50654             this.fireEvent('actionfailed', this, action);
50655         }
50656         
50657     },
50658
50659     /**
50660      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50661      * @param {String} id The value to search for
50662      * @return Field
50663      */
50664     findField : function(id){
50665         var field = this.items.get(id);
50666         if(!field){
50667             this.items.each(function(f){
50668                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50669                     field = f;
50670                     return false;
50671                 }
50672             });
50673         }
50674         return field || null;
50675     },
50676
50677     /**
50678      * Add a secondary form to this one, 
50679      * Used to provide tabbed forms. One form is primary, with hidden values 
50680      * which mirror the elements from the other forms.
50681      * 
50682      * @param {Roo.form.Form} form to add.
50683      * 
50684      */
50685     addForm : function(form)
50686     {
50687        
50688         if (this.childForms.indexOf(form) > -1) {
50689             // already added..
50690             return;
50691         }
50692         this.childForms.push(form);
50693         var n = '';
50694         Roo.each(form.allItems, function (fe) {
50695             
50696             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50697             if (this.findField(n)) { // already added..
50698                 return;
50699             }
50700             var add = new Roo.form.Hidden({
50701                 name : n
50702             });
50703             add.render(this.el);
50704             
50705             this.add( add );
50706         }, this);
50707         
50708     },
50709     /**
50710      * Mark fields in this form invalid in bulk.
50711      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50712      * @return {BasicForm} this
50713      */
50714     markInvalid : function(errors){
50715         if(errors instanceof Array){
50716             for(var i = 0, len = errors.length; i < len; i++){
50717                 var fieldError = errors[i];
50718                 var f = this.findField(fieldError.id);
50719                 if(f){
50720                     f.markInvalid(fieldError.msg);
50721                 }
50722             }
50723         }else{
50724             var field, id;
50725             for(id in errors){
50726                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50727                     field.markInvalid(errors[id]);
50728                 }
50729             }
50730         }
50731         Roo.each(this.childForms || [], function (f) {
50732             f.markInvalid(errors);
50733         });
50734         
50735         return this;
50736     },
50737
50738     /**
50739      * Set values for fields in this form in bulk.
50740      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50741      * @return {BasicForm} this
50742      */
50743     setValues : function(values){
50744         if(values instanceof Array){ // array of objects
50745             for(var i = 0, len = values.length; i < len; i++){
50746                 var v = values[i];
50747                 var f = this.findField(v.id);
50748                 if(f){
50749                     f.setValue(v.value);
50750                     if(this.trackResetOnLoad){
50751                         f.originalValue = f.getValue();
50752                     }
50753                 }
50754             }
50755         }else{ // object hash
50756             var field, id;
50757             for(id in values){
50758                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50759                     
50760                     if (field.setFromData && 
50761                         field.valueField && 
50762                         field.displayField &&
50763                         // combos' with local stores can 
50764                         // be queried via setValue()
50765                         // to set their value..
50766                         (field.store && !field.store.isLocal)
50767                         ) {
50768                         // it's a combo
50769                         var sd = { };
50770                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50771                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50772                         field.setFromData(sd);
50773                         
50774                     } else {
50775                         field.setValue(values[id]);
50776                     }
50777                     
50778                     
50779                     if(this.trackResetOnLoad){
50780                         field.originalValue = field.getValue();
50781                     }
50782                 }
50783             }
50784         }
50785         this.resetHasChanged();
50786         
50787         
50788         Roo.each(this.childForms || [], function (f) {
50789             f.setValues(values);
50790             f.resetHasChanged();
50791         });
50792                 
50793         return this;
50794     },
50795  
50796     /**
50797      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50798      * they are returned as an array.
50799      * @param {Boolean} asString
50800      * @return {Object}
50801      */
50802     getValues : function(asString)
50803     {
50804         if (this.childForms) {
50805             // copy values from the child forms
50806             Roo.each(this.childForms, function (f) {
50807                 this.setValues(f.getFieldValues()); // get the full set of data, as we might be copying comboboxes from external into this one.
50808             }, this);
50809         }
50810         
50811         // use formdata
50812         if (typeof(FormData) != 'undefined' && asString !== true) {
50813             // this relies on a 'recent' version of chrome apparently...
50814             try {
50815                 var fd = (new FormData(this.el.dom)).entries();
50816                 var ret = {};
50817                 var ent = fd.next();
50818                 while (!ent.done) {
50819                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50820                     ent = fd.next();
50821                 };
50822                 return ret;
50823             } catch(e) {
50824                 
50825             }
50826             
50827         }
50828         
50829         
50830         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50831         if(asString === true){
50832             return fs;
50833         }
50834         return Roo.urlDecode(fs);
50835     },
50836     
50837     /**
50838      * Returns the fields in this form as an object with key/value pairs. 
50839      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50840      * Normally this will not return readOnly data 
50841      * @param {Boolean} with_readonly return readonly field data.
50842      * @return {Object}
50843      */
50844     getFieldValues : function(with_readonly)
50845     {
50846         if (this.childForms) {
50847             // copy values from the child forms
50848             // should this call getFieldValues - probably not as we do not currently copy
50849             // hidden fields when we generate..
50850             Roo.each(this.childForms, function (f) {
50851                 this.setValues(f.getFieldValues());
50852             }, this);
50853         }
50854         
50855         var ret = {};
50856         this.items.each(function(f){
50857             
50858             if (f.readOnly && with_readonly !== true) {
50859                 return; // skip read only values. - this is in theory to stop 'old' values being copied over new ones
50860                         // if a subform contains a copy of them.
50861                         // if you have subforms with the same editable data, you will need to copy the data back
50862                         // and forth.
50863             }
50864             
50865             if (!f.getName()) {
50866                 return;
50867             }
50868             var v = f.getValue();
50869             if (f.inputType =='radio') {
50870                 if (typeof(ret[f.getName()]) == 'undefined') {
50871                     ret[f.getName()] = ''; // empty..
50872                 }
50873                 
50874                 if (!f.el.dom.checked) {
50875                     return;
50876                     
50877                 }
50878                 v = f.el.dom.value;
50879                 
50880             }
50881             
50882             // not sure if this supported any more..
50883             if ((typeof(v) == 'object') && f.getRawValue) {
50884                 v = f.getRawValue() ; // dates..
50885             }
50886             // combo boxes where name != hiddenName...
50887             if (f.name != f.getName()) {
50888                 ret[f.name] = f.getRawValue();
50889             }
50890             ret[f.getName()] = v;
50891         });
50892         
50893         return ret;
50894     },
50895
50896     /**
50897      * Clears all invalid messages in this form.
50898      * @return {BasicForm} this
50899      */
50900     clearInvalid : function(){
50901         this.items.each(function(f){
50902            f.clearInvalid();
50903         });
50904         
50905         Roo.each(this.childForms || [], function (f) {
50906             f.clearInvalid();
50907         });
50908         
50909         
50910         return this;
50911     },
50912
50913     /**
50914      * Resets this form.
50915      * @return {BasicForm} this
50916      */
50917     reset : function(){
50918         this.items.each(function(f){
50919             f.reset();
50920         });
50921         
50922         Roo.each(this.childForms || [], function (f) {
50923             f.reset();
50924         });
50925         this.resetHasChanged();
50926         
50927         return this;
50928     },
50929
50930     /**
50931      * Add Roo.form components to this form.
50932      * @param {Field} field1
50933      * @param {Field} field2 (optional)
50934      * @param {Field} etc (optional)
50935      * @return {BasicForm} this
50936      */
50937     add : function(){
50938         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50939         return this;
50940     },
50941
50942
50943     /**
50944      * Removes a field from the items collection (does NOT remove its markup).
50945      * @param {Field} field
50946      * @return {BasicForm} this
50947      */
50948     remove : function(field){
50949         this.items.remove(field);
50950         return this;
50951     },
50952
50953     /**
50954      * Looks at the fields in this form, checks them for an id attribute,
50955      * and calls applyTo on the existing dom element with that id.
50956      * @return {BasicForm} this
50957      */
50958     render : function(){
50959         this.items.each(function(f){
50960             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50961                 f.applyTo(f.id);
50962             }
50963         });
50964         return this;
50965     },
50966
50967     /**
50968      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50969      * @param {Object} values
50970      * @return {BasicForm} this
50971      */
50972     applyToFields : function(o){
50973         this.items.each(function(f){
50974            Roo.apply(f, o);
50975         });
50976         return this;
50977     },
50978
50979     /**
50980      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50981      * @param {Object} values
50982      * @return {BasicForm} this
50983      */
50984     applyIfToFields : function(o){
50985         this.items.each(function(f){
50986            Roo.applyIf(f, o);
50987         });
50988         return this;
50989     }
50990 });
50991
50992 // back compat
50993 Roo.BasicForm = Roo.form.BasicForm;
50994
50995 Roo.apply(Roo.form.BasicForm, {
50996     
50997     popover : {
50998         
50999         padding : 5,
51000         
51001         isApplied : false,
51002         
51003         isMasked : false,
51004         
51005         form : false,
51006         
51007         target : false,
51008         
51009         intervalID : false,
51010         
51011         maskEl : false,
51012         
51013         apply : function()
51014         {
51015             if(this.isApplied){
51016                 return;
51017             }
51018             
51019             this.maskEl = {
51020                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
51021                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
51022                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
51023                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
51024             };
51025             
51026             this.maskEl.top.enableDisplayMode("block");
51027             this.maskEl.left.enableDisplayMode("block");
51028             this.maskEl.bottom.enableDisplayMode("block");
51029             this.maskEl.right.enableDisplayMode("block");
51030             
51031             Roo.get(document.body).on('click', function(){
51032                 this.unmask();
51033             }, this);
51034             
51035             Roo.get(document.body).on('touchstart', function(){
51036                 this.unmask();
51037             }, this);
51038             
51039             this.isApplied = true
51040         },
51041         
51042         mask : function(form, target)
51043         {
51044             this.form = form;
51045             
51046             this.target = target;
51047             
51048             if(!this.form.errorMask || !target.el){
51049                 return;
51050             }
51051             
51052             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
51053             
51054             var ot = this.target.el.calcOffsetsTo(scrollable);
51055             
51056             var scrollTo = ot[1] - this.form.maskOffset;
51057             
51058             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
51059             
51060             scrollable.scrollTo('top', scrollTo);
51061             
51062             var el = this.target.wrap || this.target.el;
51063             
51064             var box = el.getBox();
51065             
51066             this.maskEl.top.setStyle('position', 'absolute');
51067             this.maskEl.top.setStyle('z-index', 10000);
51068             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
51069             this.maskEl.top.setLeft(0);
51070             this.maskEl.top.setTop(0);
51071             this.maskEl.top.show();
51072             
51073             this.maskEl.left.setStyle('position', 'absolute');
51074             this.maskEl.left.setStyle('z-index', 10000);
51075             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
51076             this.maskEl.left.setLeft(0);
51077             this.maskEl.left.setTop(box.y - this.padding);
51078             this.maskEl.left.show();
51079
51080             this.maskEl.bottom.setStyle('position', 'absolute');
51081             this.maskEl.bottom.setStyle('z-index', 10000);
51082             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
51083             this.maskEl.bottom.setLeft(0);
51084             this.maskEl.bottom.setTop(box.bottom + this.padding);
51085             this.maskEl.bottom.show();
51086
51087             this.maskEl.right.setStyle('position', 'absolute');
51088             this.maskEl.right.setStyle('z-index', 10000);
51089             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
51090             this.maskEl.right.setLeft(box.right + this.padding);
51091             this.maskEl.right.setTop(box.y - this.padding);
51092             this.maskEl.right.show();
51093
51094             this.intervalID = window.setInterval(function() {
51095                 Roo.form.BasicForm.popover.unmask();
51096             }, 10000);
51097
51098             window.onwheel = function(){ return false;};
51099             
51100             (function(){ this.isMasked = true; }).defer(500, this);
51101             
51102         },
51103         
51104         unmask : function()
51105         {
51106             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
51107                 return;
51108             }
51109             
51110             this.maskEl.top.setStyle('position', 'absolute');
51111             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
51112             this.maskEl.top.hide();
51113
51114             this.maskEl.left.setStyle('position', 'absolute');
51115             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
51116             this.maskEl.left.hide();
51117
51118             this.maskEl.bottom.setStyle('position', 'absolute');
51119             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
51120             this.maskEl.bottom.hide();
51121
51122             this.maskEl.right.setStyle('position', 'absolute');
51123             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
51124             this.maskEl.right.hide();
51125             
51126             window.onwheel = function(){ return true;};
51127             
51128             if(this.intervalID){
51129                 window.clearInterval(this.intervalID);
51130                 this.intervalID = false;
51131             }
51132             
51133             this.isMasked = false;
51134             
51135         }
51136         
51137     }
51138     
51139 });/*
51140  * Based on:
51141  * Ext JS Library 1.1.1
51142  * Copyright(c) 2006-2007, Ext JS, LLC.
51143  *
51144  * Originally Released Under LGPL - original licence link has changed is not relivant.
51145  *
51146  * Fork - LGPL
51147  * <script type="text/javascript">
51148  */
51149
51150 /**
51151  * @class Roo.form.Form
51152  * @extends Roo.form.BasicForm
51153  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51154  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
51155  * @constructor
51156  * @param {Object} config Configuration options
51157  */
51158 Roo.form.Form = function(config){
51159     var xitems =  [];
51160     if (config.items) {
51161         xitems = config.items;
51162         delete config.items;
51163     }
51164    
51165     
51166     Roo.form.Form.superclass.constructor.call(this, null, config);
51167     this.url = this.url || this.action;
51168     if(!this.root){
51169         this.root = new Roo.form.Layout(Roo.applyIf({
51170             id: Roo.id()
51171         }, config));
51172     }
51173     this.active = this.root;
51174     /**
51175      * Array of all the buttons that have been added to this form via {@link addButton}
51176      * @type Array
51177      */
51178     this.buttons = [];
51179     this.allItems = [];
51180     this.addEvents({
51181         /**
51182          * @event clientvalidation
51183          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
51184          * @param {Form} this
51185          * @param {Boolean} valid true if the form has passed client-side validation
51186          */
51187         clientvalidation: true,
51188         /**
51189          * @event rendered
51190          * Fires when the form is rendered
51191          * @param {Roo.form.Form} form
51192          */
51193         rendered : true
51194     });
51195     
51196     if (this.progressUrl) {
51197             // push a hidden field onto the list of fields..
51198             this.addxtype( {
51199                     xns: Roo.form, 
51200                     xtype : 'Hidden', 
51201                     name : 'UPLOAD_IDENTIFIER' 
51202             });
51203         }
51204         
51205     
51206     Roo.each(xitems, this.addxtype, this);
51207     
51208 };
51209
51210 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
51211      /**
51212      * @cfg {Roo.Button} buttons[] buttons at bottom of form
51213      */
51214     
51215     /**
51216      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
51217      */
51218     /**
51219      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
51220      */
51221     /**
51222      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
51223      */
51224     buttonAlign:'center',
51225
51226     /**
51227      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
51228      */
51229     minButtonWidth:75,
51230
51231     /**
51232      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
51233      * This property cascades to child containers if not set.
51234      */
51235     labelAlign:'left',
51236
51237     /**
51238      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
51239      * fires a looping event with that state. This is required to bind buttons to the valid
51240      * state using the config value formBind:true on the button.
51241      */
51242     monitorValid : false,
51243
51244     /**
51245      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
51246      */
51247     monitorPoll : 200,
51248     
51249     /**
51250      * @cfg {String} progressUrl - Url to return progress data 
51251      */
51252     
51253     progressUrl : false,
51254     /**
51255      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
51256      * sending a formdata with extra parameters - eg uploaded elements.
51257      */
51258     
51259     formData : false,
51260     
51261     /**
51262      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
51263      * fields are added and the column is closed. If no fields are passed the column remains open
51264      * until end() is called.
51265      * @param {Object} config The config to pass to the column
51266      * @param {Field} field1 (optional)
51267      * @param {Field} field2 (optional)
51268      * @param {Field} etc (optional)
51269      * @return Column The column container object
51270      */
51271     column : function(c){
51272         var col = new Roo.form.Column(c);
51273         this.start(col);
51274         if(arguments.length > 1){ // duplicate code required because of Opera
51275             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51276             this.end();
51277         }
51278         return col;
51279     },
51280
51281     /**
51282      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
51283      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
51284      * until end() is called.
51285      * @param {Object} config The config to pass to the fieldset
51286      * @param {Field} field1 (optional)
51287      * @param {Field} field2 (optional)
51288      * @param {Field} etc (optional)
51289      * @return FieldSet The fieldset container object
51290      */
51291     fieldset : function(c){
51292         var fs = new Roo.form.FieldSet(c);
51293         this.start(fs);
51294         if(arguments.length > 1){ // duplicate code required because of Opera
51295             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51296             this.end();
51297         }
51298         return fs;
51299     },
51300
51301     /**
51302      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
51303      * fields are added and the container is closed. If no fields are passed the container remains open
51304      * until end() is called.
51305      * @param {Object} config The config to pass to the Layout
51306      * @param {Field} field1 (optional)
51307      * @param {Field} field2 (optional)
51308      * @param {Field} etc (optional)
51309      * @return Layout The container object
51310      */
51311     container : function(c){
51312         var l = new Roo.form.Layout(c);
51313         this.start(l);
51314         if(arguments.length > 1){ // duplicate code required because of Opera
51315             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
51316             this.end();
51317         }
51318         return l;
51319     },
51320
51321     /**
51322      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
51323      * @param {Object} container A Roo.form.Layout or subclass of Layout
51324      * @return {Form} this
51325      */
51326     start : function(c){
51327         // cascade label info
51328         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
51329         this.active.stack.push(c);
51330         c.ownerCt = this.active;
51331         this.active = c;
51332         return this;
51333     },
51334
51335     /**
51336      * Closes the current open container
51337      * @return {Form} this
51338      */
51339     end : function(){
51340         if(this.active == this.root){
51341             return this;
51342         }
51343         this.active = this.active.ownerCt;
51344         return this;
51345     },
51346
51347     /**
51348      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
51349      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
51350      * as the label of the field.
51351      * @param {Field} field1
51352      * @param {Field} field2 (optional)
51353      * @param {Field} etc. (optional)
51354      * @return {Form} this
51355      */
51356     add : function(){
51357         this.active.stack.push.apply(this.active.stack, arguments);
51358         this.allItems.push.apply(this.allItems,arguments);
51359         var r = [];
51360         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
51361             if(a[i].isFormField){
51362                 r.push(a[i]);
51363             }
51364         }
51365         if(r.length > 0){
51366             Roo.form.Form.superclass.add.apply(this, r);
51367         }
51368         return this;
51369     },
51370     
51371
51372     
51373     
51374     
51375      /**
51376      * Find any element that has been added to a form, using it's ID or name
51377      * This can include framesets, columns etc. along with regular fields..
51378      * @param {String} id - id or name to find.
51379      
51380      * @return {Element} e - or false if nothing found.
51381      */
51382     findbyId : function(id)
51383     {
51384         var ret = false;
51385         if (!id) {
51386             return ret;
51387         }
51388         Roo.each(this.allItems, function(f){
51389             if (f.id == id || f.name == id ){
51390                 ret = f;
51391                 return false;
51392             }
51393         });
51394         return ret;
51395     },
51396
51397     
51398     
51399     /**
51400      * Render this form into the passed container. This should only be called once!
51401      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51402      * @return {Form} this
51403      */
51404     render : function(ct)
51405     {
51406         
51407         
51408         
51409         ct = Roo.get(ct);
51410         var o = this.autoCreate || {
51411             tag: 'form',
51412             method : this.method || 'POST',
51413             id : this.id || Roo.id()
51414         };
51415         this.initEl(ct.createChild(o));
51416
51417         this.root.render(this.el);
51418         
51419        
51420              
51421         this.items.each(function(f){
51422             f.render('x-form-el-'+f.id);
51423         });
51424
51425         if(this.buttons.length > 0){
51426             // tables are required to maintain order and for correct IE layout
51427             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51428                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51429                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51430             }}, null, true);
51431             var tr = tb.getElementsByTagName('tr')[0];
51432             for(var i = 0, len = this.buttons.length; i < len; i++) {
51433                 var b = this.buttons[i];
51434                 var td = document.createElement('td');
51435                 td.className = 'x-form-btn-td';
51436                 b.render(tr.appendChild(td));
51437             }
51438         }
51439         if(this.monitorValid){ // initialize after render
51440             this.startMonitoring();
51441         }
51442         this.fireEvent('rendered', this);
51443         return this;
51444     },
51445
51446     /**
51447      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51448      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51449      * object or a valid Roo.DomHelper element config
51450      * @param {Function} handler The function called when the button is clicked
51451      * @param {Object} scope (optional) The scope of the handler function
51452      * @return {Roo.Button}
51453      */
51454     addButton : function(config, handler, scope){
51455         var bc = {
51456             handler: handler,
51457             scope: scope,
51458             minWidth: this.minButtonWidth,
51459             hideParent:true
51460         };
51461         if(typeof config == "string"){
51462             bc.text = config;
51463         }else{
51464             Roo.apply(bc, config);
51465         }
51466         var btn = new Roo.Button(null, bc);
51467         this.buttons.push(btn);
51468         return btn;
51469     },
51470
51471      /**
51472      * Adds a series of form elements (using the xtype property as the factory method.
51473      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51474      * @param {Object} config 
51475      */
51476     
51477     addxtype : function()
51478     {
51479         var ar = Array.prototype.slice.call(arguments, 0);
51480         var ret = false;
51481         for(var i = 0; i < ar.length; i++) {
51482             if (!ar[i]) {
51483                 continue; // skip -- if this happends something invalid got sent, we 
51484                 // should ignore it, as basically that interface element will not show up
51485                 // and that should be pretty obvious!!
51486             }
51487             
51488             if (Roo.form[ar[i].xtype]) {
51489                 ar[i].form = this;
51490                 var fe = Roo.factory(ar[i], Roo.form);
51491                 if (!ret) {
51492                     ret = fe;
51493                 }
51494                 fe.form = this;
51495                 if (fe.store) {
51496                     fe.store.form = this;
51497                 }
51498                 if (fe.isLayout) {  
51499                          
51500                     this.start(fe);
51501                     this.allItems.push(fe);
51502                     if (fe.items && fe.addxtype) {
51503                         fe.addxtype.apply(fe, fe.items);
51504                         delete fe.items;
51505                     }
51506                      this.end();
51507                     continue;
51508                 }
51509                 
51510                 
51511                  
51512                 this.add(fe);
51513               //  console.log('adding ' + ar[i].xtype);
51514             }
51515             if (ar[i].xtype == 'Button') {  
51516                 //console.log('adding button');
51517                 //console.log(ar[i]);
51518                 this.addButton(ar[i]);
51519                 this.allItems.push(fe);
51520                 continue;
51521             }
51522             
51523             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51524                 alert('end is not supported on xtype any more, use items');
51525             //    this.end();
51526             //    //console.log('adding end');
51527             }
51528             
51529         }
51530         return ret;
51531     },
51532     
51533     /**
51534      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51535      * option "monitorValid"
51536      */
51537     startMonitoring : function(){
51538         if(!this.bound){
51539             this.bound = true;
51540             Roo.TaskMgr.start({
51541                 run : this.bindHandler,
51542                 interval : this.monitorPoll || 200,
51543                 scope: this
51544             });
51545         }
51546     },
51547
51548     /**
51549      * Stops monitoring of the valid state of this form
51550      */
51551     stopMonitoring : function(){
51552         this.bound = false;
51553     },
51554
51555     // private
51556     bindHandler : function(){
51557         if(!this.bound){
51558             return false; // stops binding
51559         }
51560         var valid = true;
51561         this.items.each(function(f){
51562             if(!f.isValid(true)){
51563                 valid = false;
51564                 return false;
51565             }
51566         });
51567         for(var i = 0, len = this.buttons.length; i < len; i++){
51568             var btn = this.buttons[i];
51569             if(btn.formBind === true && btn.disabled === valid){
51570                 btn.setDisabled(!valid);
51571             }
51572         }
51573         this.fireEvent('clientvalidation', this, valid);
51574     }
51575     
51576     
51577     
51578     
51579     
51580     
51581     
51582     
51583 });
51584
51585
51586 // back compat
51587 Roo.Form = Roo.form.Form;
51588 /*
51589  * Based on:
51590  * Ext JS Library 1.1.1
51591  * Copyright(c) 2006-2007, Ext JS, LLC.
51592  *
51593  * Originally Released Under LGPL - original licence link has changed is not relivant.
51594  *
51595  * Fork - LGPL
51596  * <script type="text/javascript">
51597  */
51598
51599 // as we use this in bootstrap.
51600 Roo.namespace('Roo.form');
51601  /**
51602  * @class Roo.form.Action
51603  * Internal Class used to handle form actions
51604  * @constructor
51605  * @param {Roo.form.BasicForm} el The form element or its id
51606  * @param {Object} config Configuration options
51607  */
51608
51609  
51610  
51611 // define the action interface
51612 Roo.form.Action = function(form, options){
51613     this.form = form;
51614     this.options = options || {};
51615 };
51616 /**
51617  * Client Validation Failed
51618  * @const 
51619  */
51620 Roo.form.Action.CLIENT_INVALID = 'client';
51621 /**
51622  * Server Validation Failed
51623  * @const 
51624  */
51625 Roo.form.Action.SERVER_INVALID = 'server';
51626  /**
51627  * Connect to Server Failed
51628  * @const 
51629  */
51630 Roo.form.Action.CONNECT_FAILURE = 'connect';
51631 /**
51632  * Reading Data from Server Failed
51633  * @const 
51634  */
51635 Roo.form.Action.LOAD_FAILURE = 'load';
51636
51637 Roo.form.Action.prototype = {
51638     type : 'default',
51639     failureType : undefined,
51640     response : undefined,
51641     result : undefined,
51642
51643     // interface method
51644     run : function(options){
51645
51646     },
51647
51648     // interface method
51649     success : function(response){
51650
51651     },
51652
51653     // interface method
51654     handleResponse : function(response){
51655
51656     },
51657
51658     // default connection failure
51659     failure : function(response){
51660         
51661         this.response = response;
51662         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51663         this.form.afterAction(this, false);
51664     },
51665
51666     processResponse : function(response){
51667         this.response = response;
51668         if(!response.responseText){
51669             return true;
51670         }
51671         this.result = this.handleResponse(response);
51672         return this.result;
51673     },
51674
51675     // utility functions used internally
51676     getUrl : function(appendParams){
51677         var url = this.options.url || this.form.url || this.form.el.dom.action;
51678         if(appendParams){
51679             var p = this.getParams();
51680             if(p){
51681                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51682             }
51683         }
51684         return url;
51685     },
51686
51687     getMethod : function(){
51688         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51689     },
51690
51691     getParams : function(){
51692         var bp = this.form.baseParams;
51693         var p = this.options.params;
51694         if(p){
51695             if(typeof p == "object"){
51696                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51697             }else if(typeof p == 'string' && bp){
51698                 p += '&' + Roo.urlEncode(bp);
51699             }
51700         }else if(bp){
51701             p = Roo.urlEncode(bp);
51702         }
51703         return p;
51704     },
51705
51706     createCallback : function(){
51707         return {
51708             success: this.success,
51709             failure: this.failure,
51710             scope: this,
51711             timeout: (this.form.timeout*1000),
51712             upload: this.form.fileUpload ? this.success : undefined
51713         };
51714     }
51715 };
51716
51717 Roo.form.Action.Submit = function(form, options){
51718     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51719 };
51720
51721 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51722     type : 'submit',
51723
51724     haveProgress : false,
51725     uploadComplete : false,
51726     
51727     // uploadProgress indicator.
51728     uploadProgress : function()
51729     {
51730         if (!this.form.progressUrl) {
51731             return;
51732         }
51733         
51734         if (!this.haveProgress) {
51735             Roo.MessageBox.progress("Uploading", "Uploading");
51736         }
51737         if (this.uploadComplete) {
51738            Roo.MessageBox.hide();
51739            return;
51740         }
51741         
51742         this.haveProgress = true;
51743    
51744         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51745         
51746         var c = new Roo.data.Connection();
51747         c.request({
51748             url : this.form.progressUrl,
51749             params: {
51750                 id : uid
51751             },
51752             method: 'GET',
51753             success : function(req){
51754                //console.log(data);
51755                 var rdata = false;
51756                 var edata;
51757                 try  {
51758                    rdata = Roo.decode(req.responseText)
51759                 } catch (e) {
51760                     Roo.log("Invalid data from server..");
51761                     Roo.log(edata);
51762                     return;
51763                 }
51764                 if (!rdata || !rdata.success) {
51765                     Roo.log(rdata);
51766                     Roo.MessageBox.alert(Roo.encode(rdata));
51767                     return;
51768                 }
51769                 var data = rdata.data;
51770                 
51771                 if (this.uploadComplete) {
51772                    Roo.MessageBox.hide();
51773                    return;
51774                 }
51775                    
51776                 if (data){
51777                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51778                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51779                     );
51780                 }
51781                 this.uploadProgress.defer(2000,this);
51782             },
51783        
51784             failure: function(data) {
51785                 Roo.log('progress url failed ');
51786                 Roo.log(data);
51787             },
51788             scope : this
51789         });
51790            
51791     },
51792     
51793     
51794     run : function()
51795     {
51796         // run get Values on the form, so it syncs any secondary forms.
51797         this.form.getValues();
51798         
51799         var o = this.options;
51800         var method = this.getMethod();
51801         var isPost = method == 'POST';
51802         if(o.clientValidation === false || this.form.isValid()){
51803             
51804             if (this.form.progressUrl) {
51805                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51806                     (new Date() * 1) + '' + Math.random());
51807                     
51808             } 
51809             
51810             
51811             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51812                 form:this.form.el.dom,
51813                 url:this.getUrl(!isPost),
51814                 method: method,
51815                 params:isPost ? this.getParams() : null,
51816                 isUpload: this.form.fileUpload,
51817                 formData : this.form.formData
51818             }));
51819             
51820             this.uploadProgress();
51821
51822         }else if (o.clientValidation !== false){ // client validation failed
51823             this.failureType = Roo.form.Action.CLIENT_INVALID;
51824             this.form.afterAction(this, false);
51825         }
51826     },
51827
51828     success : function(response)
51829     {
51830         this.uploadComplete= true;
51831         if (this.haveProgress) {
51832             Roo.MessageBox.hide();
51833         }
51834         
51835         
51836         var result = this.processResponse(response);
51837         if(result === true || result.success){
51838             this.form.afterAction(this, true);
51839             return;
51840         }
51841         if(result.errors){
51842             this.form.markInvalid(result.errors);
51843             this.failureType = Roo.form.Action.SERVER_INVALID;
51844         }
51845         this.form.afterAction(this, false);
51846     },
51847     failure : function(response)
51848     {
51849         this.uploadComplete= true;
51850         if (this.haveProgress) {
51851             Roo.MessageBox.hide();
51852         }
51853         
51854         this.response = response;
51855         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51856         this.form.afterAction(this, false);
51857     },
51858     
51859     handleResponse : function(response){
51860         if(this.form.errorReader){
51861             var rs = this.form.errorReader.read(response);
51862             var errors = [];
51863             if(rs.records){
51864                 for(var i = 0, len = rs.records.length; i < len; i++) {
51865                     var r = rs.records[i];
51866                     errors[i] = r.data;
51867                 }
51868             }
51869             if(errors.length < 1){
51870                 errors = null;
51871             }
51872             return {
51873                 success : rs.success,
51874                 errors : errors
51875             };
51876         }
51877         var ret = false;
51878         try {
51879             ret = Roo.decode(response.responseText);
51880         } catch (e) {
51881             ret = {
51882                 success: false,
51883                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51884                 errors : []
51885             };
51886         }
51887         return ret;
51888         
51889     }
51890 });
51891
51892
51893 Roo.form.Action.Load = function(form, options){
51894     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51895     this.reader = this.form.reader;
51896 };
51897
51898 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51899     type : 'load',
51900
51901     run : function(){
51902         
51903         Roo.Ajax.request(Roo.apply(
51904                 this.createCallback(), {
51905                     method:this.getMethod(),
51906                     url:this.getUrl(false),
51907                     params:this.getParams()
51908         }));
51909     },
51910
51911     success : function(response){
51912         
51913         var result = this.processResponse(response);
51914         if(result === true || !result.success || !result.data){
51915             this.failureType = Roo.form.Action.LOAD_FAILURE;
51916             this.form.afterAction(this, false);
51917             return;
51918         }
51919         this.form.clearInvalid();
51920         this.form.setValues(result.data);
51921         this.form.afterAction(this, true);
51922     },
51923
51924     handleResponse : function(response){
51925         if(this.form.reader){
51926             var rs = this.form.reader.read(response);
51927             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51928             return {
51929                 success : rs.success,
51930                 data : data
51931             };
51932         }
51933         return Roo.decode(response.responseText);
51934     }
51935 });
51936
51937 Roo.form.Action.ACTION_TYPES = {
51938     'load' : Roo.form.Action.Load,
51939     'submit' : Roo.form.Action.Submit
51940 };/*
51941  * Based on:
51942  * Ext JS Library 1.1.1
51943  * Copyright(c) 2006-2007, Ext JS, LLC.
51944  *
51945  * Originally Released Under LGPL - original licence link has changed is not relivant.
51946  *
51947  * Fork - LGPL
51948  * <script type="text/javascript">
51949  */
51950  
51951 /**
51952  * @class Roo.form.Layout
51953  * @extends Roo.Component
51954  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
51955  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51956  * @constructor
51957  * @param {Object} config Configuration options
51958  */
51959 Roo.form.Layout = function(config){
51960     var xitems = [];
51961     if (config.items) {
51962         xitems = config.items;
51963         delete config.items;
51964     }
51965     Roo.form.Layout.superclass.constructor.call(this, config);
51966     this.stack = [];
51967     Roo.each(xitems, this.addxtype, this);
51968      
51969 };
51970
51971 Roo.extend(Roo.form.Layout, Roo.Component, {
51972     /**
51973      * @cfg {String/Object} autoCreate
51974      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51975      */
51976     /**
51977      * @cfg {String/Object/Function} style
51978      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51979      * a function which returns such a specification.
51980      */
51981     /**
51982      * @cfg {String} labelAlign
51983      * Valid values are "left," "top" and "right" (defaults to "left")
51984      */
51985     /**
51986      * @cfg {Number} labelWidth
51987      * Fixed width in pixels of all field labels (defaults to undefined)
51988      */
51989     /**
51990      * @cfg {Boolean} clear
51991      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51992      */
51993     clear : true,
51994     /**
51995      * @cfg {String} labelSeparator
51996      * The separator to use after field labels (defaults to ':')
51997      */
51998     labelSeparator : ':',
51999     /**
52000      * @cfg {Boolean} hideLabels
52001      * True to suppress the display of field labels in this layout (defaults to false)
52002      */
52003     hideLabels : false,
52004
52005     // private
52006     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
52007     
52008     isLayout : true,
52009     
52010     // private
52011     onRender : function(ct, position){
52012         if(this.el){ // from markup
52013             this.el = Roo.get(this.el);
52014         }else {  // generate
52015             var cfg = this.getAutoCreate();
52016             this.el = ct.createChild(cfg, position);
52017         }
52018         if(this.style){
52019             this.el.applyStyles(this.style);
52020         }
52021         if(this.labelAlign){
52022             this.el.addClass('x-form-label-'+this.labelAlign);
52023         }
52024         if(this.hideLabels){
52025             this.labelStyle = "display:none";
52026             this.elementStyle = "padding-left:0;";
52027         }else{
52028             if(typeof this.labelWidth == 'number'){
52029                 this.labelStyle = "width:"+this.labelWidth+"px;";
52030                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
52031             }
52032             if(this.labelAlign == 'top'){
52033                 this.labelStyle = "width:auto;";
52034                 this.elementStyle = "padding-left:0;";
52035             }
52036         }
52037         var stack = this.stack;
52038         var slen = stack.length;
52039         if(slen > 0){
52040             if(!this.fieldTpl){
52041                 var t = new Roo.Template(
52042                     '<div class="x-form-item {5}">',
52043                         '<label for="{0}" style="{2}">{1}{4}</label>',
52044                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52045                         '</div>',
52046                     '</div><div class="x-form-clear-left"></div>'
52047                 );
52048                 t.disableFormats = true;
52049                 t.compile();
52050                 Roo.form.Layout.prototype.fieldTpl = t;
52051             }
52052             for(var i = 0; i < slen; i++) {
52053                 if(stack[i].isFormField){
52054                     this.renderField(stack[i]);
52055                 }else{
52056                     this.renderComponent(stack[i]);
52057                 }
52058             }
52059         }
52060         if(this.clear){
52061             this.el.createChild({cls:'x-form-clear'});
52062         }
52063     },
52064
52065     // private
52066     renderField : function(f){
52067         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
52068                f.id, //0
52069                f.fieldLabel, //1
52070                f.labelStyle||this.labelStyle||'', //2
52071                this.elementStyle||'', //3
52072                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
52073                f.itemCls||this.itemCls||''  //5
52074        ], true).getPrevSibling());
52075     },
52076
52077     // private
52078     renderComponent : function(c){
52079         c.render(c.isLayout ? this.el : this.el.createChild());    
52080     },
52081     /**
52082      * Adds a object form elements (using the xtype property as the factory method.)
52083      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
52084      * @param {Object} config 
52085      */
52086     addxtype : function(o)
52087     {
52088         // create the lement.
52089         o.form = this.form;
52090         var fe = Roo.factory(o, Roo.form);
52091         this.form.allItems.push(fe);
52092         this.stack.push(fe);
52093         
52094         if (fe.isFormField) {
52095             this.form.items.add(fe);
52096         }
52097          
52098         return fe;
52099     }
52100 });
52101
52102 /**
52103  * @class Roo.form.Column
52104  * @extends Roo.form.Layout
52105  * @children Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52106  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
52107  * @constructor
52108  * @param {Object} config Configuration options
52109  */
52110 Roo.form.Column = function(config){
52111     Roo.form.Column.superclass.constructor.call(this, config);
52112 };
52113
52114 Roo.extend(Roo.form.Column, Roo.form.Layout, {
52115     /**
52116      * @cfg {Number/String} width
52117      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52118      */
52119     /**
52120      * @cfg {String/Object} autoCreate
52121      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
52122      */
52123
52124     // private
52125     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
52126
52127     // private
52128     onRender : function(ct, position){
52129         Roo.form.Column.superclass.onRender.call(this, ct, position);
52130         if(this.width){
52131             this.el.setWidth(this.width);
52132         }
52133     }
52134 });
52135
52136
52137 /**
52138  * @class Roo.form.Row
52139  * @extends Roo.form.Layout
52140  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem Roo.form.FieldSet
52141  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
52142  * @constructor
52143  * @param {Object} config Configuration options
52144  */
52145
52146  
52147 Roo.form.Row = function(config){
52148     Roo.form.Row.superclass.constructor.call(this, config);
52149 };
52150  
52151 Roo.extend(Roo.form.Row, Roo.form.Layout, {
52152       /**
52153      * @cfg {Number/String} width
52154      * The fixed width of the column in pixels or CSS value (defaults to "auto")
52155      */
52156     /**
52157      * @cfg {Number/String} height
52158      * The fixed height of the column in pixels or CSS value (defaults to "auto")
52159      */
52160     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
52161     
52162     padWidth : 20,
52163     // private
52164     onRender : function(ct, position){
52165         //console.log('row render');
52166         if(!this.rowTpl){
52167             var t = new Roo.Template(
52168                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
52169                     '<label for="{0}" style="{2}">{1}{4}</label>',
52170                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
52171                     '</div>',
52172                 '</div>'
52173             );
52174             t.disableFormats = true;
52175             t.compile();
52176             Roo.form.Layout.prototype.rowTpl = t;
52177         }
52178         this.fieldTpl = this.rowTpl;
52179         
52180         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
52181         var labelWidth = 100;
52182         
52183         if ((this.labelAlign != 'top')) {
52184             if (typeof this.labelWidth == 'number') {
52185                 labelWidth = this.labelWidth
52186             }
52187             this.padWidth =  20 + labelWidth;
52188             
52189         }
52190         
52191         Roo.form.Column.superclass.onRender.call(this, ct, position);
52192         if(this.width){
52193             this.el.setWidth(this.width);
52194         }
52195         if(this.height){
52196             this.el.setHeight(this.height);
52197         }
52198     },
52199     
52200     // private
52201     renderField : function(f){
52202         f.fieldEl = this.fieldTpl.append(this.el, [
52203                f.id, f.fieldLabel,
52204                f.labelStyle||this.labelStyle||'',
52205                this.elementStyle||'',
52206                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
52207                f.itemCls||this.itemCls||'',
52208                f.width ? f.width + this.padWidth : 160 + this.padWidth
52209        ],true);
52210     }
52211 });
52212  
52213
52214 /**
52215  * @class Roo.form.FieldSet
52216  * @extends Roo.form.Layout
52217  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
52218  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
52219  * @constructor
52220  * @param {Object} config Configuration options
52221  */
52222 Roo.form.FieldSet = function(config){
52223     Roo.form.FieldSet.superclass.constructor.call(this, config);
52224 };
52225
52226 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
52227     /**
52228      * @cfg {String} legend
52229      * The text to display as the legend for the FieldSet (defaults to '')
52230      */
52231     /**
52232      * @cfg {String/Object} autoCreate
52233      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
52234      */
52235
52236     // private
52237     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
52238
52239     // private
52240     onRender : function(ct, position){
52241         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
52242         if(this.legend){
52243             this.setLegend(this.legend);
52244         }
52245     },
52246
52247     // private
52248     setLegend : function(text){
52249         if(this.rendered){
52250             this.el.child('legend').update(text);
52251         }
52252     }
52253 });/*
52254  * Based on:
52255  * Ext JS Library 1.1.1
52256  * Copyright(c) 2006-2007, Ext JS, LLC.
52257  *
52258  * Originally Released Under LGPL - original licence link has changed is not relivant.
52259  *
52260  * Fork - LGPL
52261  * <script type="text/javascript">
52262  */
52263 /**
52264  * @class Roo.form.VTypes
52265  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
52266  * @static
52267  */
52268 Roo.form.VTypes = function(){
52269     // closure these in so they are only created once.
52270     var alpha = /^[a-zA-Z_]+$/;
52271     var alphanum = /^[a-zA-Z0-9_]+$/;
52272     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
52273     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
52274
52275     // All these messages and functions are configurable
52276     return {
52277         /**
52278          * The function used to validate email addresses
52279          * @param {String} value The email address
52280          */
52281         'email' : function(v){
52282             return email.test(v);
52283         },
52284         /**
52285          * The error text to display when the email validation function returns false
52286          * @type String
52287          */
52288         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
52289         /**
52290          * The keystroke filter mask to be applied on email input
52291          * @type RegExp
52292          */
52293         'emailMask' : /[a-z0-9_\.\-@]/i,
52294
52295         /**
52296          * The function used to validate URLs
52297          * @param {String} value The URL
52298          */
52299         'url' : function(v){
52300             return url.test(v);
52301         },
52302         /**
52303          * The error text to display when the url validation function returns false
52304          * @type String
52305          */
52306         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
52307         
52308         /**
52309          * The function used to validate alpha values
52310          * @param {String} value The value
52311          */
52312         'alpha' : function(v){
52313             return alpha.test(v);
52314         },
52315         /**
52316          * The error text to display when the alpha validation function returns false
52317          * @type String
52318          */
52319         'alphaText' : 'This field should only contain letters and _',
52320         /**
52321          * The keystroke filter mask to be applied on alpha input
52322          * @type RegExp
52323          */
52324         'alphaMask' : /[a-z_]/i,
52325
52326         /**
52327          * The function used to validate alphanumeric values
52328          * @param {String} value The value
52329          */
52330         'alphanum' : function(v){
52331             return alphanum.test(v);
52332         },
52333         /**
52334          * The error text to display when the alphanumeric validation function returns false
52335          * @type String
52336          */
52337         'alphanumText' : 'This field should only contain letters, numbers and _',
52338         /**
52339          * The keystroke filter mask to be applied on alphanumeric input
52340          * @type RegExp
52341          */
52342         'alphanumMask' : /[a-z0-9_]/i
52343     };
52344 }();//<script type="text/javascript">
52345
52346 /**
52347  * @class Roo.form.FCKeditor
52348  * @extends Roo.form.TextArea
52349  * Wrapper around the FCKEditor http://www.fckeditor.net
52350  * @constructor
52351  * Creates a new FCKeditor
52352  * @param {Object} config Configuration options
52353  */
52354 Roo.form.FCKeditor = function(config){
52355     Roo.form.FCKeditor.superclass.constructor.call(this, config);
52356     this.addEvents({
52357          /**
52358          * @event editorinit
52359          * Fired when the editor is initialized - you can add extra handlers here..
52360          * @param {FCKeditor} this
52361          * @param {Object} the FCK object.
52362          */
52363         editorinit : true
52364     });
52365     
52366     
52367 };
52368 Roo.form.FCKeditor.editors = { };
52369 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
52370 {
52371     //defaultAutoCreate : {
52372     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
52373     //},
52374     // private
52375     /**
52376      * @cfg {Object} fck options - see fck manual for details.
52377      */
52378     fckconfig : false,
52379     
52380     /**
52381      * @cfg {Object} fck toolbar set (Basic or Default)
52382      */
52383     toolbarSet : 'Basic',
52384     /**
52385      * @cfg {Object} fck BasePath
52386      */ 
52387     basePath : '/fckeditor/',
52388     
52389     
52390     frame : false,
52391     
52392     value : '',
52393     
52394    
52395     onRender : function(ct, position)
52396     {
52397         if(!this.el){
52398             this.defaultAutoCreate = {
52399                 tag: "textarea",
52400                 style:"width:300px;height:60px;",
52401                 autocomplete: "new-password"
52402             };
52403         }
52404         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52405         /*
52406         if(this.grow){
52407             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52408             if(this.preventScrollbars){
52409                 this.el.setStyle("overflow", "hidden");
52410             }
52411             this.el.setHeight(this.growMin);
52412         }
52413         */
52414         //console.log('onrender' + this.getId() );
52415         Roo.form.FCKeditor.editors[this.getId()] = this;
52416          
52417
52418         this.replaceTextarea() ;
52419         
52420     },
52421     
52422     getEditor : function() {
52423         return this.fckEditor;
52424     },
52425     /**
52426      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52427      * @param {Mixed} value The value to set
52428      */
52429     
52430     
52431     setValue : function(value)
52432     {
52433         //console.log('setValue: ' + value);
52434         
52435         if(typeof(value) == 'undefined') { // not sure why this is happending...
52436             return;
52437         }
52438         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52439         
52440         //if(!this.el || !this.getEditor()) {
52441         //    this.value = value;
52442             //this.setValue.defer(100,this,[value]);    
52443         //    return;
52444         //} 
52445         
52446         if(!this.getEditor()) {
52447             return;
52448         }
52449         
52450         this.getEditor().SetData(value);
52451         
52452         //
52453
52454     },
52455
52456     /**
52457      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52458      * @return {Mixed} value The field value
52459      */
52460     getValue : function()
52461     {
52462         
52463         if (this.frame && this.frame.dom.style.display == 'none') {
52464             return Roo.form.FCKeditor.superclass.getValue.call(this);
52465         }
52466         
52467         if(!this.el || !this.getEditor()) {
52468            
52469            // this.getValue.defer(100,this); 
52470             return this.value;
52471         }
52472        
52473         
52474         var value=this.getEditor().GetData();
52475         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52476         return Roo.form.FCKeditor.superclass.getValue.call(this);
52477         
52478
52479     },
52480
52481     /**
52482      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52483      * @return {Mixed} value The field value
52484      */
52485     getRawValue : function()
52486     {
52487         if (this.frame && this.frame.dom.style.display == 'none') {
52488             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52489         }
52490         
52491         if(!this.el || !this.getEditor()) {
52492             //this.getRawValue.defer(100,this); 
52493             return this.value;
52494             return;
52495         }
52496         
52497         
52498         
52499         var value=this.getEditor().GetData();
52500         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52501         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52502          
52503     },
52504     
52505     setSize : function(w,h) {
52506         
52507         
52508         
52509         //if (this.frame && this.frame.dom.style.display == 'none') {
52510         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52511         //    return;
52512         //}
52513         //if(!this.el || !this.getEditor()) {
52514         //    this.setSize.defer(100,this, [w,h]); 
52515         //    return;
52516         //}
52517         
52518         
52519         
52520         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52521         
52522         this.frame.dom.setAttribute('width', w);
52523         this.frame.dom.setAttribute('height', h);
52524         this.frame.setSize(w,h);
52525         
52526     },
52527     
52528     toggleSourceEdit : function(value) {
52529         
52530       
52531          
52532         this.el.dom.style.display = value ? '' : 'none';
52533         this.frame.dom.style.display = value ?  'none' : '';
52534         
52535     },
52536     
52537     
52538     focus: function(tag)
52539     {
52540         if (this.frame.dom.style.display == 'none') {
52541             return Roo.form.FCKeditor.superclass.focus.call(this);
52542         }
52543         if(!this.el || !this.getEditor()) {
52544             this.focus.defer(100,this, [tag]); 
52545             return;
52546         }
52547         
52548         
52549         
52550         
52551         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52552         this.getEditor().Focus();
52553         if (tgs.length) {
52554             if (!this.getEditor().Selection.GetSelection()) {
52555                 this.focus.defer(100,this, [tag]); 
52556                 return;
52557             }
52558             
52559             
52560             var r = this.getEditor().EditorDocument.createRange();
52561             r.setStart(tgs[0],0);
52562             r.setEnd(tgs[0],0);
52563             this.getEditor().Selection.GetSelection().removeAllRanges();
52564             this.getEditor().Selection.GetSelection().addRange(r);
52565             this.getEditor().Focus();
52566         }
52567         
52568     },
52569     
52570     
52571     
52572     replaceTextarea : function()
52573     {
52574         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52575             return ;
52576         }
52577         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52578         //{
52579             // We must check the elements firstly using the Id and then the name.
52580         var oTextarea = document.getElementById( this.getId() );
52581         
52582         var colElementsByName = document.getElementsByName( this.getId() ) ;
52583          
52584         oTextarea.style.display = 'none' ;
52585
52586         if ( oTextarea.tabIndex ) {            
52587             this.TabIndex = oTextarea.tabIndex ;
52588         }
52589         
52590         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52591         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52592         this.frame = Roo.get(this.getId() + '___Frame')
52593     },
52594     
52595     _getConfigHtml : function()
52596     {
52597         var sConfig = '' ;
52598
52599         for ( var o in this.fckconfig ) {
52600             sConfig += sConfig.length > 0  ? '&amp;' : '';
52601             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52602         }
52603
52604         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52605     },
52606     
52607     
52608     _getIFrameHtml : function()
52609     {
52610         var sFile = 'fckeditor.html' ;
52611         /* no idea what this is about..
52612         try
52613         {
52614             if ( (/fcksource=true/i).test( window.top.location.search ) )
52615                 sFile = 'fckeditor.original.html' ;
52616         }
52617         catch (e) { 
52618         */
52619
52620         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52621         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52622         
52623         
52624         var html = '<iframe id="' + this.getId() +
52625             '___Frame" src="' + sLink +
52626             '" width="' + this.width +
52627             '" height="' + this.height + '"' +
52628             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52629             ' frameborder="0" scrolling="no"></iframe>' ;
52630
52631         return html ;
52632     },
52633     
52634     _insertHtmlBefore : function( html, element )
52635     {
52636         if ( element.insertAdjacentHTML )       {
52637             // IE
52638             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52639         } else { // Gecko
52640             var oRange = document.createRange() ;
52641             oRange.setStartBefore( element ) ;
52642             var oFragment = oRange.createContextualFragment( html );
52643             element.parentNode.insertBefore( oFragment, element ) ;
52644         }
52645     }
52646     
52647     
52648   
52649     
52650     
52651     
52652     
52653
52654 });
52655
52656 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52657
52658 function FCKeditor_OnComplete(editorInstance){
52659     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52660     f.fckEditor = editorInstance;
52661     //console.log("loaded");
52662     f.fireEvent('editorinit', f, editorInstance);
52663
52664   
52665
52666  
52667
52668
52669
52670
52671
52672
52673
52674
52675
52676
52677
52678
52679
52680
52681
52682 //<script type="text/javascript">
52683 /**
52684  * @class Roo.form.GridField
52685  * @extends Roo.form.Field
52686  * Embed a grid (or editable grid into a form)
52687  * STATUS ALPHA
52688  * 
52689  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52690  * it needs 
52691  * xgrid.store = Roo.data.Store
52692  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52693  * xgrid.store.reader = Roo.data.JsonReader 
52694  * 
52695  * 
52696  * @constructor
52697  * Creates a new GridField
52698  * @param {Object} config Configuration options
52699  */
52700 Roo.form.GridField = function(config){
52701     Roo.form.GridField.superclass.constructor.call(this, config);
52702      
52703 };
52704
52705 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52706     /**
52707      * @cfg {Number} width  - used to restrict width of grid..
52708      */
52709     width : 100,
52710     /**
52711      * @cfg {Number} height - used to restrict height of grid..
52712      */
52713     height : 50,
52714      /**
52715      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52716          * 
52717          *}
52718      */
52719     xgrid : false, 
52720     /**
52721      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52722      * {tag: "input", type: "checkbox", autocomplete: "off"})
52723      */
52724    // defaultAutoCreate : { tag: 'div' },
52725     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52726     /**
52727      * @cfg {String} addTitle Text to include for adding a title.
52728      */
52729     addTitle : false,
52730     //
52731     onResize : function(){
52732         Roo.form.Field.superclass.onResize.apply(this, arguments);
52733     },
52734
52735     initEvents : function(){
52736         // Roo.form.Checkbox.superclass.initEvents.call(this);
52737         // has no events...
52738        
52739     },
52740
52741
52742     getResizeEl : function(){
52743         return this.wrap;
52744     },
52745
52746     getPositionEl : function(){
52747         return this.wrap;
52748     },
52749
52750     // private
52751     onRender : function(ct, position){
52752         
52753         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52754         var style = this.style;
52755         delete this.style;
52756         
52757         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52758         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52759         this.viewEl = this.wrap.createChild({ tag: 'div' });
52760         if (style) {
52761             this.viewEl.applyStyles(style);
52762         }
52763         if (this.width) {
52764             this.viewEl.setWidth(this.width);
52765         }
52766         if (this.height) {
52767             this.viewEl.setHeight(this.height);
52768         }
52769         //if(this.inputValue !== undefined){
52770         //this.setValue(this.value);
52771         
52772         
52773         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52774         
52775         
52776         this.grid.render();
52777         this.grid.getDataSource().on('remove', this.refreshValue, this);
52778         this.grid.getDataSource().on('update', this.refreshValue, this);
52779         this.grid.on('afteredit', this.refreshValue, this);
52780  
52781     },
52782      
52783     
52784     /**
52785      * Sets the value of the item. 
52786      * @param {String} either an object  or a string..
52787      */
52788     setValue : function(v){
52789         //this.value = v;
52790         v = v || []; // empty set..
52791         // this does not seem smart - it really only affects memoryproxy grids..
52792         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52793             var ds = this.grid.getDataSource();
52794             // assumes a json reader..
52795             var data = {}
52796             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52797             ds.loadData( data);
52798         }
52799         // clear selection so it does not get stale.
52800         if (this.grid.sm) { 
52801             this.grid.sm.clearSelections();
52802         }
52803         
52804         Roo.form.GridField.superclass.setValue.call(this, v);
52805         this.refreshValue();
52806         // should load data in the grid really....
52807     },
52808     
52809     // private
52810     refreshValue: function() {
52811          var val = [];
52812         this.grid.getDataSource().each(function(r) {
52813             val.push(r.data);
52814         });
52815         this.el.dom.value = Roo.encode(val);
52816     }
52817     
52818      
52819     
52820     
52821 });/*
52822  * Based on:
52823  * Ext JS Library 1.1.1
52824  * Copyright(c) 2006-2007, Ext JS, LLC.
52825  *
52826  * Originally Released Under LGPL - original licence link has changed is not relivant.
52827  *
52828  * Fork - LGPL
52829  * <script type="text/javascript">
52830  */
52831 /**
52832  * @class Roo.form.DisplayField
52833  * @extends Roo.form.Field
52834  * A generic Field to display non-editable data.
52835  * @cfg {Boolean} closable (true|false) default false
52836  * @constructor
52837  * Creates a new Display Field item.
52838  * @param {Object} config Configuration options
52839  */
52840 Roo.form.DisplayField = function(config){
52841     Roo.form.DisplayField.superclass.constructor.call(this, config);
52842     
52843     this.addEvents({
52844         /**
52845          * @event close
52846          * Fires after the click the close btn
52847              * @param {Roo.form.DisplayField} this
52848              */
52849         close : true
52850     });
52851 };
52852
52853 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52854     inputType:      'hidden',
52855     allowBlank:     true,
52856     readOnly:         true,
52857     
52858  
52859     /**
52860      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52861      */
52862     focusClass : undefined,
52863     /**
52864      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52865      */
52866     fieldClass: 'x-form-field',
52867     
52868      /**
52869      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52870      */
52871     valueRenderer: undefined,
52872     
52873     width: 100,
52874     /**
52875      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52876      * {tag: "input", type: "checkbox", autocomplete: "off"})
52877      */
52878      
52879  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52880  
52881     closable : false,
52882     
52883     onResize : function(){
52884         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52885         
52886     },
52887
52888     initEvents : function(){
52889         // Roo.form.Checkbox.superclass.initEvents.call(this);
52890         // has no events...
52891         
52892         if(this.closable){
52893             this.closeEl.on('click', this.onClose, this);
52894         }
52895        
52896     },
52897
52898
52899     getResizeEl : function(){
52900         return this.wrap;
52901     },
52902
52903     getPositionEl : function(){
52904         return this.wrap;
52905     },
52906
52907     // private
52908     onRender : function(ct, position){
52909         
52910         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52911         //if(this.inputValue !== undefined){
52912         this.wrap = this.el.wrap();
52913         
52914         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52915         
52916         if(this.closable){
52917             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52918         }
52919         
52920         if (this.bodyStyle) {
52921             this.viewEl.applyStyles(this.bodyStyle);
52922         }
52923         //this.viewEl.setStyle('padding', '2px');
52924         
52925         this.setValue(this.value);
52926         
52927     },
52928 /*
52929     // private
52930     initValue : Roo.emptyFn,
52931
52932   */
52933
52934         // private
52935     onClick : function(){
52936         
52937     },
52938
52939     /**
52940      * Sets the checked state of the checkbox.
52941      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52942      */
52943     setValue : function(v){
52944         this.value = v;
52945         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52946         // this might be called before we have a dom element..
52947         if (!this.viewEl) {
52948             return;
52949         }
52950         this.viewEl.dom.innerHTML = html;
52951         Roo.form.DisplayField.superclass.setValue.call(this, v);
52952
52953     },
52954     
52955     onClose : function(e)
52956     {
52957         e.preventDefault();
52958         
52959         this.fireEvent('close', this);
52960     }
52961 });/*
52962  * 
52963  * Licence- LGPL
52964  * 
52965  */
52966
52967 /**
52968  * @class Roo.form.DayPicker
52969  * @extends Roo.form.Field
52970  * A Day picker show [M] [T] [W] ....
52971  * @constructor
52972  * Creates a new Day Picker
52973  * @param {Object} config Configuration options
52974  */
52975 Roo.form.DayPicker= function(config){
52976     Roo.form.DayPicker.superclass.constructor.call(this, config);
52977      
52978 };
52979
52980 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52981     /**
52982      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52983      */
52984     focusClass : undefined,
52985     /**
52986      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52987      */
52988     fieldClass: "x-form-field",
52989    
52990     /**
52991      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52992      * {tag: "input", type: "checkbox", autocomplete: "off"})
52993      */
52994     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52995     
52996    
52997     actionMode : 'viewEl', 
52998     //
52999     // private
53000  
53001     inputType : 'hidden',
53002     
53003      
53004     inputElement: false, // real input element?
53005     basedOn: false, // ????
53006     
53007     isFormField: true, // not sure where this is needed!!!!
53008
53009     onResize : function(){
53010         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
53011         if(!this.boxLabel){
53012             this.el.alignTo(this.wrap, 'c-c');
53013         }
53014     },
53015
53016     initEvents : function(){
53017         Roo.form.Checkbox.superclass.initEvents.call(this);
53018         this.el.on("click", this.onClick,  this);
53019         this.el.on("change", this.onClick,  this);
53020     },
53021
53022
53023     getResizeEl : function(){
53024         return this.wrap;
53025     },
53026
53027     getPositionEl : function(){
53028         return this.wrap;
53029     },
53030
53031     
53032     // private
53033     onRender : function(ct, position){
53034         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
53035        
53036         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
53037         
53038         var r1 = '<table><tr>';
53039         var r2 = '<tr class="x-form-daypick-icons">';
53040         for (var i=0; i < 7; i++) {
53041             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
53042             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
53043         }
53044         
53045         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
53046         viewEl.select('img').on('click', this.onClick, this);
53047         this.viewEl = viewEl;   
53048         
53049         
53050         // this will not work on Chrome!!!
53051         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
53052         this.el.on('propertychange', this.setFromHidden,  this);  //ie
53053         
53054         
53055           
53056
53057     },
53058
53059     // private
53060     initValue : Roo.emptyFn,
53061
53062     /**
53063      * Returns the checked state of the checkbox.
53064      * @return {Boolean} True if checked, else false
53065      */
53066     getValue : function(){
53067         return this.el.dom.value;
53068         
53069     },
53070
53071         // private
53072     onClick : function(e){ 
53073         //this.setChecked(!this.checked);
53074         Roo.get(e.target).toggleClass('x-menu-item-checked');
53075         this.refreshValue();
53076         //if(this.el.dom.checked != this.checked){
53077         //    this.setValue(this.el.dom.checked);
53078        // }
53079     },
53080     
53081     // private
53082     refreshValue : function()
53083     {
53084         var val = '';
53085         this.viewEl.select('img',true).each(function(e,i,n)  {
53086             val += e.is(".x-menu-item-checked") ? String(n) : '';
53087         });
53088         this.setValue(val, true);
53089     },
53090
53091     /**
53092      * Sets the checked state of the checkbox.
53093      * On is always based on a string comparison between inputValue and the param.
53094      * @param {Boolean/String} value - the value to set 
53095      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
53096      */
53097     setValue : function(v,suppressEvent){
53098         if (!this.el.dom) {
53099             return;
53100         }
53101         var old = this.el.dom.value ;
53102         this.el.dom.value = v;
53103         if (suppressEvent) {
53104             return ;
53105         }
53106          
53107         // update display..
53108         this.viewEl.select('img',true).each(function(e,i,n)  {
53109             
53110             var on = e.is(".x-menu-item-checked");
53111             var newv = v.indexOf(String(n)) > -1;
53112             if (on != newv) {
53113                 e.toggleClass('x-menu-item-checked');
53114             }
53115             
53116         });
53117         
53118         
53119         this.fireEvent('change', this, v, old);
53120         
53121         
53122     },
53123    
53124     // handle setting of hidden value by some other method!!?!?
53125     setFromHidden: function()
53126     {
53127         if(!this.el){
53128             return;
53129         }
53130         //console.log("SET FROM HIDDEN");
53131         //alert('setFrom hidden');
53132         this.setValue(this.el.dom.value);
53133     },
53134     
53135     onDestroy : function()
53136     {
53137         if(this.viewEl){
53138             Roo.get(this.viewEl).remove();
53139         }
53140          
53141         Roo.form.DayPicker.superclass.onDestroy.call(this);
53142     }
53143
53144 });/*
53145  * RooJS Library 1.1.1
53146  * Copyright(c) 2008-2011  Alan Knowles
53147  *
53148  * License - LGPL
53149  */
53150  
53151
53152 /**
53153  * @class Roo.form.ComboCheck
53154  * @extends Roo.form.ComboBox
53155  * A combobox for multiple select items.
53156  *
53157  * FIXME - could do with a reset button..
53158  * 
53159  * @constructor
53160  * Create a new ComboCheck
53161  * @param {Object} config Configuration options
53162  */
53163 Roo.form.ComboCheck = function(config){
53164     Roo.form.ComboCheck.superclass.constructor.call(this, config);
53165     // should verify some data...
53166     // like
53167     // hiddenName = required..
53168     // displayField = required
53169     // valudField == required
53170     var req= [ 'hiddenName', 'displayField', 'valueField' ];
53171     var _t = this;
53172     Roo.each(req, function(e) {
53173         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
53174             throw "Roo.form.ComboCheck : missing value for: " + e;
53175         }
53176     });
53177     
53178     
53179 };
53180
53181 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
53182      
53183      
53184     editable : false,
53185      
53186     selectedClass: 'x-menu-item-checked', 
53187     
53188     // private
53189     onRender : function(ct, position){
53190         var _t = this;
53191         
53192         
53193         
53194         if(!this.tpl){
53195             var cls = 'x-combo-list';
53196
53197             
53198             this.tpl =  new Roo.Template({
53199                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
53200                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
53201                    '<span>{' + this.displayField + '}</span>' +
53202                     '</div>' 
53203                 
53204             });
53205         }
53206  
53207         
53208         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
53209         this.view.singleSelect = false;
53210         this.view.multiSelect = true;
53211         this.view.toggleSelect = true;
53212         this.pageTb.add(new Roo.Toolbar.Fill(), {
53213             
53214             text: 'Done',
53215             handler: function()
53216             {
53217                 _t.collapse();
53218             }
53219         });
53220     },
53221     
53222     onViewOver : function(e, t){
53223         // do nothing...
53224         return;
53225         
53226     },
53227     
53228     onViewClick : function(doFocus,index){
53229         return;
53230         
53231     },
53232     select: function () {
53233         //Roo.log("SELECT CALLED");
53234     },
53235      
53236     selectByValue : function(xv, scrollIntoView){
53237         var ar = this.getValueArray();
53238         var sels = [];
53239         
53240         Roo.each(ar, function(v) {
53241             if(v === undefined || v === null){
53242                 return;
53243             }
53244             var r = this.findRecord(this.valueField, v);
53245             if(r){
53246                 sels.push(this.store.indexOf(r))
53247                 
53248             }
53249         },this);
53250         this.view.select(sels);
53251         return false;
53252     },
53253     
53254     
53255     
53256     onSelect : function(record, index){
53257        // Roo.log("onselect Called");
53258        // this is only called by the clear button now..
53259         this.view.clearSelections();
53260         this.setValue('[]');
53261         if (this.value != this.valueBefore) {
53262             this.fireEvent('change', this, this.value, this.valueBefore);
53263             this.valueBefore = this.value;
53264         }
53265     },
53266     getValueArray : function()
53267     {
53268         var ar = [] ;
53269         
53270         try {
53271             //Roo.log(this.value);
53272             if (typeof(this.value) == 'undefined') {
53273                 return [];
53274             }
53275             var ar = Roo.decode(this.value);
53276             return  ar instanceof Array ? ar : []; //?? valid?
53277             
53278         } catch(e) {
53279             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
53280             return [];
53281         }
53282          
53283     },
53284     expand : function ()
53285     {
53286         
53287         Roo.form.ComboCheck.superclass.expand.call(this);
53288         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
53289         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
53290         
53291
53292     },
53293     
53294     collapse : function(){
53295         Roo.form.ComboCheck.superclass.collapse.call(this);
53296         var sl = this.view.getSelectedIndexes();
53297         var st = this.store;
53298         var nv = [];
53299         var tv = [];
53300         var r;
53301         Roo.each(sl, function(i) {
53302             r = st.getAt(i);
53303             nv.push(r.get(this.valueField));
53304         },this);
53305         this.setValue(Roo.encode(nv));
53306         if (this.value != this.valueBefore) {
53307
53308             this.fireEvent('change', this, this.value, this.valueBefore);
53309             this.valueBefore = this.value;
53310         }
53311         
53312     },
53313     
53314     setValue : function(v){
53315         // Roo.log(v);
53316         this.value = v;
53317         
53318         var vals = this.getValueArray();
53319         var tv = [];
53320         Roo.each(vals, function(k) {
53321             var r = this.findRecord(this.valueField, k);
53322             if(r){
53323                 tv.push(r.data[this.displayField]);
53324             }else if(this.valueNotFoundText !== undefined){
53325                 tv.push( this.valueNotFoundText );
53326             }
53327         },this);
53328        // Roo.log(tv);
53329         
53330         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
53331         this.hiddenField.value = v;
53332         this.value = v;
53333     }
53334     
53335 });/*
53336  * Based on:
53337  * Ext JS Library 1.1.1
53338  * Copyright(c) 2006-2007, Ext JS, LLC.
53339  *
53340  * Originally Released Under LGPL - original licence link has changed is not relivant.
53341  *
53342  * Fork - LGPL
53343  * <script type="text/javascript">
53344  */
53345  
53346 /**
53347  * @class Roo.form.Signature
53348  * @extends Roo.form.Field
53349  * Signature field.  
53350  * @constructor
53351  * 
53352  * @param {Object} config Configuration options
53353  */
53354
53355 Roo.form.Signature = function(config){
53356     Roo.form.Signature.superclass.constructor.call(this, config);
53357     
53358     this.addEvents({// not in used??
53359          /**
53360          * @event confirm
53361          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
53362              * @param {Roo.form.Signature} combo This combo box
53363              */
53364         'confirm' : true,
53365         /**
53366          * @event reset
53367          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
53368              * @param {Roo.form.ComboBox} combo This combo box
53369              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
53370              */
53371         'reset' : true
53372     });
53373 };
53374
53375 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
53376     /**
53377      * @cfg {Object} labels Label to use when rendering a form.
53378      * defaults to 
53379      * labels : { 
53380      *      clear : "Clear",
53381      *      confirm : "Confirm"
53382      *  }
53383      */
53384     labels : { 
53385         clear : "Clear",
53386         confirm : "Confirm"
53387     },
53388     /**
53389      * @cfg {Number} width The signature panel width (defaults to 300)
53390      */
53391     width: 300,
53392     /**
53393      * @cfg {Number} height The signature panel height (defaults to 100)
53394      */
53395     height : 100,
53396     /**
53397      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53398      */
53399     allowBlank : false,
53400     
53401     //private
53402     // {Object} signPanel The signature SVG panel element (defaults to {})
53403     signPanel : {},
53404     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53405     isMouseDown : false,
53406     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53407     isConfirmed : false,
53408     // {String} signatureTmp SVG mapping string (defaults to empty string)
53409     signatureTmp : '',
53410     
53411     
53412     defaultAutoCreate : { // modified by initCompnoent..
53413         tag: "input",
53414         type:"hidden"
53415     },
53416
53417     // private
53418     onRender : function(ct, position){
53419         
53420         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53421         
53422         this.wrap = this.el.wrap({
53423             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53424         });
53425         
53426         this.createToolbar(this);
53427         this.signPanel = this.wrap.createChild({
53428                 tag: 'div',
53429                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53430             }, this.el
53431         );
53432             
53433         this.svgID = Roo.id();
53434         this.svgEl = this.signPanel.createChild({
53435               xmlns : 'http://www.w3.org/2000/svg',
53436               tag : 'svg',
53437               id : this.svgID + "-svg",
53438               width: this.width,
53439               height: this.height,
53440               viewBox: '0 0 '+this.width+' '+this.height,
53441               cn : [
53442                 {
53443                     tag: "rect",
53444                     id: this.svgID + "-svg-r",
53445                     width: this.width,
53446                     height: this.height,
53447                     fill: "#ffa"
53448                 },
53449                 {
53450                     tag: "line",
53451                     id: this.svgID + "-svg-l",
53452                     x1: "0", // start
53453                     y1: (this.height*0.8), // start set the line in 80% of height
53454                     x2: this.width, // end
53455                     y2: (this.height*0.8), // end set the line in 80% of height
53456                     'stroke': "#666",
53457                     'stroke-width': "1",
53458                     'stroke-dasharray': "3",
53459                     'shape-rendering': "crispEdges",
53460                     'pointer-events': "none"
53461                 },
53462                 {
53463                     tag: "path",
53464                     id: this.svgID + "-svg-p",
53465                     'stroke': "navy",
53466                     'stroke-width': "3",
53467                     'fill': "none",
53468                     'pointer-events': 'none'
53469                 }
53470               ]
53471         });
53472         this.createSVG();
53473         this.svgBox = this.svgEl.dom.getScreenCTM();
53474     },
53475     createSVG : function(){ 
53476         var svg = this.signPanel;
53477         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53478         var t = this;
53479
53480         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53481         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53482         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53483         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53484         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53485         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53486         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53487         
53488     },
53489     isTouchEvent : function(e){
53490         return e.type.match(/^touch/);
53491     },
53492     getCoords : function (e) {
53493         var pt    = this.svgEl.dom.createSVGPoint();
53494         pt.x = e.clientX; 
53495         pt.y = e.clientY;
53496         if (this.isTouchEvent(e)) {
53497             pt.x =  e.targetTouches[0].clientX;
53498             pt.y = e.targetTouches[0].clientY;
53499         }
53500         var a = this.svgEl.dom.getScreenCTM();
53501         var b = a.inverse();
53502         var mx = pt.matrixTransform(b);
53503         return mx.x + ',' + mx.y;
53504     },
53505     //mouse event headler 
53506     down : function (e) {
53507         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53508         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53509         
53510         this.isMouseDown = true;
53511         
53512         e.preventDefault();
53513     },
53514     move : function (e) {
53515         if (this.isMouseDown) {
53516             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53517             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53518         }
53519         
53520         e.preventDefault();
53521     },
53522     up : function (e) {
53523         this.isMouseDown = false;
53524         var sp = this.signatureTmp.split(' ');
53525         
53526         if(sp.length > 1){
53527             if(!sp[sp.length-2].match(/^L/)){
53528                 sp.pop();
53529                 sp.pop();
53530                 sp.push("");
53531                 this.signatureTmp = sp.join(" ");
53532             }
53533         }
53534         if(this.getValue() != this.signatureTmp){
53535             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53536             this.isConfirmed = false;
53537         }
53538         e.preventDefault();
53539     },
53540     
53541     /**
53542      * Protected method that will not generally be called directly. It
53543      * is called when the editor creates its toolbar. Override this method if you need to
53544      * add custom toolbar buttons.
53545      * @param {HtmlEditor} editor
53546      */
53547     createToolbar : function(editor){
53548          function btn(id, toggle, handler){
53549             var xid = fid + '-'+ id ;
53550             return {
53551                 id : xid,
53552                 cmd : id,
53553                 cls : 'x-btn-icon x-edit-'+id,
53554                 enableToggle:toggle !== false,
53555                 scope: editor, // was editor...
53556                 handler:handler||editor.relayBtnCmd,
53557                 clickEvent:'mousedown',
53558                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53559                 tabIndex:-1
53560             };
53561         }
53562         
53563         
53564         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53565         this.tb = tb;
53566         this.tb.add(
53567            {
53568                 cls : ' x-signature-btn x-signature-'+id,
53569                 scope: editor, // was editor...
53570                 handler: this.reset,
53571                 clickEvent:'mousedown',
53572                 text: this.labels.clear
53573             },
53574             {
53575                  xtype : 'Fill',
53576                  xns: Roo.Toolbar
53577             }, 
53578             {
53579                 cls : '  x-signature-btn x-signature-'+id,
53580                 scope: editor, // was editor...
53581                 handler: this.confirmHandler,
53582                 clickEvent:'mousedown',
53583                 text: this.labels.confirm
53584             }
53585         );
53586     
53587     },
53588     //public
53589     /**
53590      * when user is clicked confirm then show this image.....
53591      * 
53592      * @return {String} Image Data URI
53593      */
53594     getImageDataURI : function(){
53595         var svg = this.svgEl.dom.parentNode.innerHTML;
53596         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53597         return src; 
53598     },
53599     /**
53600      * 
53601      * @return {Boolean} this.isConfirmed
53602      */
53603     getConfirmed : function(){
53604         return this.isConfirmed;
53605     },
53606     /**
53607      * 
53608      * @return {Number} this.width
53609      */
53610     getWidth : function(){
53611         return this.width;
53612     },
53613     /**
53614      * 
53615      * @return {Number} this.height
53616      */
53617     getHeight : function(){
53618         return this.height;
53619     },
53620     // private
53621     getSignature : function(){
53622         return this.signatureTmp;
53623     },
53624     // private
53625     reset : function(){
53626         this.signatureTmp = '';
53627         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53628         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53629         this.isConfirmed = false;
53630         Roo.form.Signature.superclass.reset.call(this);
53631     },
53632     setSignature : function(s){
53633         this.signatureTmp = s;
53634         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53635         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53636         this.setValue(s);
53637         this.isConfirmed = false;
53638         Roo.form.Signature.superclass.reset.call(this);
53639     }, 
53640     test : function(){
53641 //        Roo.log(this.signPanel.dom.contentWindow.up())
53642     },
53643     //private
53644     setConfirmed : function(){
53645         
53646         
53647         
53648 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53649     },
53650     // private
53651     confirmHandler : function(){
53652         if(!this.getSignature()){
53653             return;
53654         }
53655         
53656         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53657         this.setValue(this.getSignature());
53658         this.isConfirmed = true;
53659         
53660         this.fireEvent('confirm', this);
53661     },
53662     // private
53663     // Subclasses should provide the validation implementation by overriding this
53664     validateValue : function(value){
53665         if(this.allowBlank){
53666             return true;
53667         }
53668         
53669         if(this.isConfirmed){
53670             return true;
53671         }
53672         return false;
53673     }
53674 });/*
53675  * Based on:
53676  * Ext JS Library 1.1.1
53677  * Copyright(c) 2006-2007, Ext JS, LLC.
53678  *
53679  * Originally Released Under LGPL - original licence link has changed is not relivant.
53680  *
53681  * Fork - LGPL
53682  * <script type="text/javascript">
53683  */
53684  
53685
53686 /**
53687  * @class Roo.form.ComboBox
53688  * @extends Roo.form.TriggerField
53689  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53690  * @constructor
53691  * Create a new ComboBox.
53692  * @param {Object} config Configuration options
53693  */
53694 Roo.form.Select = function(config){
53695     Roo.form.Select.superclass.constructor.call(this, config);
53696      
53697 };
53698
53699 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53700     /**
53701      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53702      */
53703     /**
53704      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53705      * rendering into an Roo.Editor, defaults to false)
53706      */
53707     /**
53708      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53709      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53710      */
53711     /**
53712      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53713      */
53714     /**
53715      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53716      * the dropdown list (defaults to undefined, with no header element)
53717      */
53718
53719      /**
53720      * @cfg {String/Roo.Template} tpl The template to use to render the output
53721      */
53722      
53723     // private
53724     defaultAutoCreate : {tag: "select"  },
53725     /**
53726      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53727      */
53728     listWidth: undefined,
53729     /**
53730      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53731      * mode = 'remote' or 'text' if mode = 'local')
53732      */
53733     displayField: undefined,
53734     /**
53735      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53736      * mode = 'remote' or 'value' if mode = 'local'). 
53737      * Note: use of a valueField requires the user make a selection
53738      * in order for a value to be mapped.
53739      */
53740     valueField: undefined,
53741     
53742     
53743     /**
53744      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53745      * field's data value (defaults to the underlying DOM element's name)
53746      */
53747     hiddenName: undefined,
53748     /**
53749      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53750      */
53751     listClass: '',
53752     /**
53753      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53754      */
53755     selectedClass: 'x-combo-selected',
53756     /**
53757      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53758      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53759      * which displays a downward arrow icon).
53760      */
53761     triggerClass : 'x-form-arrow-trigger',
53762     /**
53763      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53764      */
53765     shadow:'sides',
53766     /**
53767      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53768      * anchor positions (defaults to 'tl-bl')
53769      */
53770     listAlign: 'tl-bl?',
53771     /**
53772      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53773      */
53774     maxHeight: 300,
53775     /**
53776      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53777      * query specified by the allQuery config option (defaults to 'query')
53778      */
53779     triggerAction: 'query',
53780     /**
53781      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53782      * (defaults to 4, does not apply if editable = false)
53783      */
53784     minChars : 4,
53785     /**
53786      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53787      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53788      */
53789     typeAhead: false,
53790     /**
53791      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53792      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53793      */
53794     queryDelay: 500,
53795     /**
53796      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53797      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53798      */
53799     pageSize: 0,
53800     /**
53801      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53802      * when editable = true (defaults to false)
53803      */
53804     selectOnFocus:false,
53805     /**
53806      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53807      */
53808     queryParam: 'query',
53809     /**
53810      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53811      * when mode = 'remote' (defaults to 'Loading...')
53812      */
53813     loadingText: 'Loading...',
53814     /**
53815      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53816      */
53817     resizable: false,
53818     /**
53819      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53820      */
53821     handleHeight : 8,
53822     /**
53823      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53824      * traditional select (defaults to true)
53825      */
53826     editable: true,
53827     /**
53828      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53829      */
53830     allQuery: '',
53831     /**
53832      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53833      */
53834     mode: 'remote',
53835     /**
53836      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53837      * listWidth has a higher value)
53838      */
53839     minListWidth : 70,
53840     /**
53841      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53842      * allow the user to set arbitrary text into the field (defaults to false)
53843      */
53844     forceSelection:false,
53845     /**
53846      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53847      * if typeAhead = true (defaults to 250)
53848      */
53849     typeAheadDelay : 250,
53850     /**
53851      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53852      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53853      */
53854     valueNotFoundText : undefined,
53855     
53856     /**
53857      * @cfg {String} defaultValue The value displayed after loading the store.
53858      */
53859     defaultValue: '',
53860     
53861     /**
53862      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53863      */
53864     blockFocus : false,
53865     
53866     /**
53867      * @cfg {Boolean} disableClear Disable showing of clear button.
53868      */
53869     disableClear : false,
53870     /**
53871      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53872      */
53873     alwaysQuery : false,
53874     
53875     //private
53876     addicon : false,
53877     editicon: false,
53878     
53879     // element that contains real text value.. (when hidden is used..)
53880      
53881     // private
53882     onRender : function(ct, position){
53883         Roo.form.Field.prototype.onRender.call(this, ct, position);
53884         
53885         if(this.store){
53886             this.store.on('beforeload', this.onBeforeLoad, this);
53887             this.store.on('load', this.onLoad, this);
53888             this.store.on('loadexception', this.onLoadException, this);
53889             this.store.load({});
53890         }
53891         
53892         
53893         
53894     },
53895
53896     // private
53897     initEvents : function(){
53898         //Roo.form.ComboBox.superclass.initEvents.call(this);
53899  
53900     },
53901
53902     onDestroy : function(){
53903        
53904         if(this.store){
53905             this.store.un('beforeload', this.onBeforeLoad, this);
53906             this.store.un('load', this.onLoad, this);
53907             this.store.un('loadexception', this.onLoadException, this);
53908         }
53909         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53910     },
53911
53912     // private
53913     fireKey : function(e){
53914         if(e.isNavKeyPress() && !this.list.isVisible()){
53915             this.fireEvent("specialkey", this, e);
53916         }
53917     },
53918
53919     // private
53920     onResize: function(w, h){
53921         
53922         return; 
53923     
53924         
53925     },
53926
53927     /**
53928      * Allow or prevent the user from directly editing the field text.  If false is passed,
53929      * the user will only be able to select from the items defined in the dropdown list.  This method
53930      * is the runtime equivalent of setting the 'editable' config option at config time.
53931      * @param {Boolean} value True to allow the user to directly edit the field text
53932      */
53933     setEditable : function(value){
53934          
53935     },
53936
53937     // private
53938     onBeforeLoad : function(){
53939         
53940         Roo.log("Select before load");
53941         return;
53942     
53943         this.innerList.update(this.loadingText ?
53944                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53945         //this.restrictHeight();
53946         this.selectedIndex = -1;
53947     },
53948
53949     // private
53950     onLoad : function(){
53951
53952     
53953         var dom = this.el.dom;
53954         dom.innerHTML = '';
53955          var od = dom.ownerDocument;
53956          
53957         if (this.emptyText) {
53958             var op = od.createElement('option');
53959             op.setAttribute('value', '');
53960             op.innerHTML = String.format('{0}', this.emptyText);
53961             dom.appendChild(op);
53962         }
53963         if(this.store.getCount() > 0){
53964            
53965             var vf = this.valueField;
53966             var df = this.displayField;
53967             this.store.data.each(function(r) {
53968                 // which colmsn to use... testing - cdoe / title..
53969                 var op = od.createElement('option');
53970                 op.setAttribute('value', r.data[vf]);
53971                 op.innerHTML = String.format('{0}', r.data[df]);
53972                 dom.appendChild(op);
53973             });
53974             if (typeof(this.defaultValue != 'undefined')) {
53975                 this.setValue(this.defaultValue);
53976             }
53977             
53978              
53979         }else{
53980             //this.onEmptyResults();
53981         }
53982         //this.el.focus();
53983     },
53984     // private
53985     onLoadException : function()
53986     {
53987         dom.innerHTML = '';
53988             
53989         Roo.log("Select on load exception");
53990         return;
53991     
53992         this.collapse();
53993         Roo.log(this.store.reader.jsonData);
53994         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53995             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53996         }
53997         
53998         
53999     },
54000     // private
54001     onTypeAhead : function(){
54002          
54003     },
54004
54005     // private
54006     onSelect : function(record, index){
54007         Roo.log('on select?');
54008         return;
54009         if(this.fireEvent('beforeselect', this, record, index) !== false){
54010             this.setFromData(index > -1 ? record.data : false);
54011             this.collapse();
54012             this.fireEvent('select', this, record, index);
54013         }
54014     },
54015
54016     /**
54017      * Returns the currently selected field value or empty string if no value is set.
54018      * @return {String} value The selected value
54019      */
54020     getValue : function(){
54021         var dom = this.el.dom;
54022         this.value = dom.options[dom.selectedIndex].value;
54023         return this.value;
54024         
54025     },
54026
54027     /**
54028      * Clears any text/value currently set in the field
54029      */
54030     clearValue : function(){
54031         this.value = '';
54032         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
54033         
54034     },
54035
54036     /**
54037      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
54038      * will be displayed in the field.  If the value does not match the data value of an existing item,
54039      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
54040      * Otherwise the field will be blank (although the value will still be set).
54041      * @param {String} value The value to match
54042      */
54043     setValue : function(v){
54044         var d = this.el.dom;
54045         for (var i =0; i < d.options.length;i++) {
54046             if (v == d.options[i].value) {
54047                 d.selectedIndex = i;
54048                 this.value = v;
54049                 return;
54050             }
54051         }
54052         this.clearValue();
54053     },
54054     /**
54055      * @property {Object} the last set data for the element
54056      */
54057     
54058     lastData : false,
54059     /**
54060      * Sets the value of the field based on a object which is related to the record format for the store.
54061      * @param {Object} value the value to set as. or false on reset?
54062      */
54063     setFromData : function(o){
54064         Roo.log('setfrom data?');
54065          
54066         
54067         
54068     },
54069     // private
54070     reset : function(){
54071         this.clearValue();
54072     },
54073     // private
54074     findRecord : function(prop, value){
54075         
54076         return false;
54077     
54078         var record;
54079         if(this.store.getCount() > 0){
54080             this.store.each(function(r){
54081                 if(r.data[prop] == value){
54082                     record = r;
54083                     return false;
54084                 }
54085                 return true;
54086             });
54087         }
54088         return record;
54089     },
54090     
54091     getName: function()
54092     {
54093         // returns hidden if it's set..
54094         if (!this.rendered) {return ''};
54095         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
54096         
54097     },
54098      
54099
54100     
54101
54102     // private
54103     onEmptyResults : function(){
54104         Roo.log('empty results');
54105         //this.collapse();
54106     },
54107
54108     /**
54109      * Returns true if the dropdown list is expanded, else false.
54110      */
54111     isExpanded : function(){
54112         return false;
54113     },
54114
54115     /**
54116      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
54117      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54118      * @param {String} value The data value of the item to select
54119      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54120      * selected item if it is not currently in view (defaults to true)
54121      * @return {Boolean} True if the value matched an item in the list, else false
54122      */
54123     selectByValue : function(v, scrollIntoView){
54124         Roo.log('select By Value');
54125         return false;
54126     
54127         if(v !== undefined && v !== null){
54128             var r = this.findRecord(this.valueField || this.displayField, v);
54129             if(r){
54130                 this.select(this.store.indexOf(r), scrollIntoView);
54131                 return true;
54132             }
54133         }
54134         return false;
54135     },
54136
54137     /**
54138      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
54139      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
54140      * @param {Number} index The zero-based index of the list item to select
54141      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
54142      * selected item if it is not currently in view (defaults to true)
54143      */
54144     select : function(index, scrollIntoView){
54145         Roo.log('select ');
54146         return  ;
54147         
54148         this.selectedIndex = index;
54149         this.view.select(index);
54150         if(scrollIntoView !== false){
54151             var el = this.view.getNode(index);
54152             if(el){
54153                 this.innerList.scrollChildIntoView(el, false);
54154             }
54155         }
54156     },
54157
54158       
54159
54160     // private
54161     validateBlur : function(){
54162         
54163         return;
54164         
54165     },
54166
54167     // private
54168     initQuery : function(){
54169         this.doQuery(this.getRawValue());
54170     },
54171
54172     // private
54173     doForce : function(){
54174         if(this.el.dom.value.length > 0){
54175             this.el.dom.value =
54176                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
54177              
54178         }
54179     },
54180
54181     /**
54182      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
54183      * query allowing the query action to be canceled if needed.
54184      * @param {String} query The SQL query to execute
54185      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
54186      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
54187      * saved in the current store (defaults to false)
54188      */
54189     doQuery : function(q, forceAll){
54190         
54191         Roo.log('doQuery?');
54192         if(q === undefined || q === null){
54193             q = '';
54194         }
54195         var qe = {
54196             query: q,
54197             forceAll: forceAll,
54198             combo: this,
54199             cancel:false
54200         };
54201         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
54202             return false;
54203         }
54204         q = qe.query;
54205         forceAll = qe.forceAll;
54206         if(forceAll === true || (q.length >= this.minChars)){
54207             if(this.lastQuery != q || this.alwaysQuery){
54208                 this.lastQuery = q;
54209                 if(this.mode == 'local'){
54210                     this.selectedIndex = -1;
54211                     if(forceAll){
54212                         this.store.clearFilter();
54213                     }else{
54214                         this.store.filter(this.displayField, q);
54215                     }
54216                     this.onLoad();
54217                 }else{
54218                     this.store.baseParams[this.queryParam] = q;
54219                     this.store.load({
54220                         params: this.getParams(q)
54221                     });
54222                     this.expand();
54223                 }
54224             }else{
54225                 this.selectedIndex = -1;
54226                 this.onLoad();   
54227             }
54228         }
54229     },
54230
54231     // private
54232     getParams : function(q){
54233         var p = {};
54234         //p[this.queryParam] = q;
54235         if(this.pageSize){
54236             p.start = 0;
54237             p.limit = this.pageSize;
54238         }
54239         return p;
54240     },
54241
54242     /**
54243      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
54244      */
54245     collapse : function(){
54246         
54247     },
54248
54249     // private
54250     collapseIf : function(e){
54251         
54252     },
54253
54254     /**
54255      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
54256      */
54257     expand : function(){
54258         
54259     } ,
54260
54261     // private
54262      
54263
54264     /** 
54265     * @cfg {Boolean} grow 
54266     * @hide 
54267     */
54268     /** 
54269     * @cfg {Number} growMin 
54270     * @hide 
54271     */
54272     /** 
54273     * @cfg {Number} growMax 
54274     * @hide 
54275     */
54276     /**
54277      * @hide
54278      * @method autoSize
54279      */
54280     
54281     setWidth : function()
54282     {
54283         
54284     },
54285     getResizeEl : function(){
54286         return this.el;
54287     }
54288 });//<script type="text/javasscript">
54289  
54290
54291 /**
54292  * @class Roo.DDView
54293  * A DnD enabled version of Roo.View.
54294  * @param {Element/String} container The Element in which to create the View.
54295  * @param {String} tpl The template string used to create the markup for each element of the View
54296  * @param {Object} config The configuration properties. These include all the config options of
54297  * {@link Roo.View} plus some specific to this class.<br>
54298  * <p>
54299  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
54300  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
54301  * <p>
54302  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
54303 .x-view-drag-insert-above {
54304         border-top:1px dotted #3366cc;
54305 }
54306 .x-view-drag-insert-below {
54307         border-bottom:1px dotted #3366cc;
54308 }
54309 </code></pre>
54310  * 
54311  */
54312  
54313 Roo.DDView = function(container, tpl, config) {
54314     Roo.DDView.superclass.constructor.apply(this, arguments);
54315     this.getEl().setStyle("outline", "0px none");
54316     this.getEl().unselectable();
54317     if (this.dragGroup) {
54318         this.setDraggable(this.dragGroup.split(","));
54319     }
54320     if (this.dropGroup) {
54321         this.setDroppable(this.dropGroup.split(","));
54322     }
54323     if (this.deletable) {
54324         this.setDeletable();
54325     }
54326     this.isDirtyFlag = false;
54327         this.addEvents({
54328                 "drop" : true
54329         });
54330 };
54331
54332 Roo.extend(Roo.DDView, Roo.View, {
54333 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
54334 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
54335 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
54336 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
54337
54338         isFormField: true,
54339
54340         reset: Roo.emptyFn,
54341         
54342         clearInvalid: Roo.form.Field.prototype.clearInvalid,
54343
54344         validate: function() {
54345                 return true;
54346         },
54347         
54348         destroy: function() {
54349                 this.purgeListeners();
54350                 this.getEl.removeAllListeners();
54351                 this.getEl().remove();
54352                 if (this.dragZone) {
54353                         if (this.dragZone.destroy) {
54354                                 this.dragZone.destroy();
54355                         }
54356                 }
54357                 if (this.dropZone) {
54358                         if (this.dropZone.destroy) {
54359                                 this.dropZone.destroy();
54360                         }
54361                 }
54362         },
54363
54364 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
54365         getName: function() {
54366                 return this.name;
54367         },
54368
54369 /**     Loads the View from a JSON string representing the Records to put into the Store. */
54370         setValue: function(v) {
54371                 if (!this.store) {
54372                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
54373                 }
54374                 var data = {};
54375                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
54376                 this.store.proxy = new Roo.data.MemoryProxy(data);
54377                 this.store.load();
54378         },
54379
54380 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54381         getValue: function() {
54382                 var result = '(';
54383                 this.store.each(function(rec) {
54384                         result += rec.id + ',';
54385                 });
54386                 return result.substr(0, result.length - 1) + ')';
54387         },
54388         
54389         getIds: function() {
54390                 var i = 0, result = new Array(this.store.getCount());
54391                 this.store.each(function(rec) {
54392                         result[i++] = rec.id;
54393                 });
54394                 return result;
54395         },
54396         
54397         isDirty: function() {
54398                 return this.isDirtyFlag;
54399         },
54400
54401 /**
54402  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54403  *      whole Element becomes the target, and this causes the drop gesture to append.
54404  */
54405     getTargetFromEvent : function(e) {
54406                 var target = e.getTarget();
54407                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54408                 target = target.parentNode;
54409                 }
54410                 if (!target) {
54411                         target = this.el.dom.lastChild || this.el.dom;
54412                 }
54413                 return target;
54414     },
54415
54416 /**
54417  *      Create the drag data which consists of an object which has the property "ddel" as
54418  *      the drag proxy element. 
54419  */
54420     getDragData : function(e) {
54421         var target = this.findItemFromChild(e.getTarget());
54422                 if(target) {
54423                         this.handleSelection(e);
54424                         var selNodes = this.getSelectedNodes();
54425             var dragData = {
54426                 source: this,
54427                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54428                 nodes: selNodes,
54429                 records: []
54430                         };
54431                         var selectedIndices = this.getSelectedIndexes();
54432                         for (var i = 0; i < selectedIndices.length; i++) {
54433                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54434                         }
54435                         if (selNodes.length == 1) {
54436                                 dragData.ddel = target.cloneNode(true); // the div element
54437                         } else {
54438                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54439                                 div.className = 'multi-proxy';
54440                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54441                                         div.appendChild(selNodes[i].cloneNode(true));
54442                                 }
54443                                 dragData.ddel = div;
54444                         }
54445             //console.log(dragData)
54446             //console.log(dragData.ddel.innerHTML)
54447                         return dragData;
54448                 }
54449         //console.log('nodragData')
54450                 return false;
54451     },
54452     
54453 /**     Specify to which ddGroup items in this DDView may be dragged. */
54454     setDraggable: function(ddGroup) {
54455         if (ddGroup instanceof Array) {
54456                 Roo.each(ddGroup, this.setDraggable, this);
54457                 return;
54458         }
54459         if (this.dragZone) {
54460                 this.dragZone.addToGroup(ddGroup);
54461         } else {
54462                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54463                                 containerScroll: true,
54464                                 ddGroup: ddGroup 
54465
54466                         });
54467 //                      Draggability implies selection. DragZone's mousedown selects the element.
54468                         if (!this.multiSelect) { this.singleSelect = true; }
54469
54470 //                      Wire the DragZone's handlers up to methods in *this*
54471                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54472                 }
54473     },
54474
54475 /**     Specify from which ddGroup this DDView accepts drops. */
54476     setDroppable: function(ddGroup) {
54477         if (ddGroup instanceof Array) {
54478                 Roo.each(ddGroup, this.setDroppable, this);
54479                 return;
54480         }
54481         if (this.dropZone) {
54482                 this.dropZone.addToGroup(ddGroup);
54483         } else {
54484                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54485                                 containerScroll: true,
54486                                 ddGroup: ddGroup
54487                         });
54488
54489 //                      Wire the DropZone's handlers up to methods in *this*
54490                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54491                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54492                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54493                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54494                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54495                 }
54496     },
54497
54498 /**     Decide whether to drop above or below a View node. */
54499     getDropPoint : function(e, n, dd){
54500         if (n == this.el.dom) { return "above"; }
54501                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54502                 var c = t + (b - t) / 2;
54503                 var y = Roo.lib.Event.getPageY(e);
54504                 if(y <= c) {
54505                         return "above";
54506                 }else{
54507                         return "below";
54508                 }
54509     },
54510
54511     onNodeEnter : function(n, dd, e, data){
54512                 return false;
54513     },
54514     
54515     onNodeOver : function(n, dd, e, data){
54516                 var pt = this.getDropPoint(e, n, dd);
54517                 // set the insert point style on the target node
54518                 var dragElClass = this.dropNotAllowed;
54519                 if (pt) {
54520                         var targetElClass;
54521                         if (pt == "above"){
54522                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54523                                 targetElClass = "x-view-drag-insert-above";
54524                         } else {
54525                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54526                                 targetElClass = "x-view-drag-insert-below";
54527                         }
54528                         if (this.lastInsertClass != targetElClass){
54529                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54530                                 this.lastInsertClass = targetElClass;
54531                         }
54532                 }
54533                 return dragElClass;
54534         },
54535
54536     onNodeOut : function(n, dd, e, data){
54537                 this.removeDropIndicators(n);
54538     },
54539
54540     onNodeDrop : function(n, dd, e, data){
54541         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54542                 return false;
54543         }
54544         var pt = this.getDropPoint(e, n, dd);
54545                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54546                 if (pt == "below") { insertAt++; }
54547                 for (var i = 0; i < data.records.length; i++) {
54548                         var r = data.records[i];
54549                         var dup = this.store.getById(r.id);
54550                         if (dup && (dd != this.dragZone)) {
54551                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54552                         } else {
54553                                 if (data.copy) {
54554                                         this.store.insert(insertAt++, r.copy());
54555                                 } else {
54556                                         data.source.isDirtyFlag = true;
54557                                         r.store.remove(r);
54558                                         this.store.insert(insertAt++, r);
54559                                 }
54560                                 this.isDirtyFlag = true;
54561                         }
54562                 }
54563                 this.dragZone.cachedTarget = null;
54564                 return true;
54565     },
54566
54567     removeDropIndicators : function(n){
54568                 if(n){
54569                         Roo.fly(n).removeClass([
54570                                 "x-view-drag-insert-above",
54571                                 "x-view-drag-insert-below"]);
54572                         this.lastInsertClass = "_noclass";
54573                 }
54574     },
54575
54576 /**
54577  *      Utility method. Add a delete option to the DDView's context menu.
54578  *      @param {String} imageUrl The URL of the "delete" icon image.
54579  */
54580         setDeletable: function(imageUrl) {
54581                 if (!this.singleSelect && !this.multiSelect) {
54582                         this.singleSelect = true;
54583                 }
54584                 var c = this.getContextMenu();
54585                 this.contextMenu.on("itemclick", function(item) {
54586                         switch (item.id) {
54587                                 case "delete":
54588                                         this.remove(this.getSelectedIndexes());
54589                                         break;
54590                         }
54591                 }, this);
54592                 this.contextMenu.add({
54593                         icon: imageUrl,
54594                         id: "delete",
54595                         text: 'Delete'
54596                 });
54597         },
54598         
54599 /**     Return the context menu for this DDView. */
54600         getContextMenu: function() {
54601                 if (!this.contextMenu) {
54602 //                      Create the View's context menu
54603                         this.contextMenu = new Roo.menu.Menu({
54604                                 id: this.id + "-contextmenu"
54605                         });
54606                         this.el.on("contextmenu", this.showContextMenu, this);
54607                 }
54608                 return this.contextMenu;
54609         },
54610         
54611         disableContextMenu: function() {
54612                 if (this.contextMenu) {
54613                         this.el.un("contextmenu", this.showContextMenu, this);
54614                 }
54615         },
54616
54617         showContextMenu: function(e, item) {
54618         item = this.findItemFromChild(e.getTarget());
54619                 if (item) {
54620                         e.stopEvent();
54621                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54622                         this.contextMenu.showAt(e.getXY());
54623             }
54624     },
54625
54626 /**
54627  *      Remove {@link Roo.data.Record}s at the specified indices.
54628  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54629  */
54630     remove: function(selectedIndices) {
54631                 selectedIndices = [].concat(selectedIndices);
54632                 for (var i = 0; i < selectedIndices.length; i++) {
54633                         var rec = this.store.getAt(selectedIndices[i]);
54634                         this.store.remove(rec);
54635                 }
54636     },
54637
54638 /**
54639  *      Double click fires the event, but also, if this is draggable, and there is only one other
54640  *      related DropZone, it transfers the selected node.
54641  */
54642     onDblClick : function(e){
54643         var item = this.findItemFromChild(e.getTarget());
54644         if(item){
54645             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54646                 return false;
54647             }
54648             if (this.dragGroup) {
54649                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54650                     while (targets.indexOf(this.dropZone) > -1) {
54651                             targets.remove(this.dropZone);
54652                                 }
54653                     if (targets.length == 1) {
54654                                         this.dragZone.cachedTarget = null;
54655                         var el = Roo.get(targets[0].getEl());
54656                         var box = el.getBox(true);
54657                         targets[0].onNodeDrop(el.dom, {
54658                                 target: el.dom,
54659                                 xy: [box.x, box.y + box.height - 1]
54660                         }, null, this.getDragData(e));
54661                     }
54662                 }
54663         }
54664     },
54665     
54666     handleSelection: function(e) {
54667                 this.dragZone.cachedTarget = null;
54668         var item = this.findItemFromChild(e.getTarget());
54669         if (!item) {
54670                 this.clearSelections(true);
54671                 return;
54672         }
54673                 if (item && (this.multiSelect || this.singleSelect)){
54674                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54675                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54676                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54677                                 this.unselect(item);
54678                         } else {
54679                                 this.select(item, this.multiSelect && e.ctrlKey);
54680                                 this.lastSelection = item;
54681                         }
54682                 }
54683     },
54684
54685     onItemClick : function(item, index, e){
54686                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54687                         return false;
54688                 }
54689                 return true;
54690     },
54691
54692     unselect : function(nodeInfo, suppressEvent){
54693                 var node = this.getNode(nodeInfo);
54694                 if(node && this.isSelected(node)){
54695                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54696                                 Roo.fly(node).removeClass(this.selectedClass);
54697                                 this.selections.remove(node);
54698                                 if(!suppressEvent){
54699                                         this.fireEvent("selectionchange", this, this.selections);
54700                                 }
54701                         }
54702                 }
54703     }
54704 });
54705 /*
54706  * Based on:
54707  * Ext JS Library 1.1.1
54708  * Copyright(c) 2006-2007, Ext JS, LLC.
54709  *
54710  * Originally Released Under LGPL - original licence link has changed is not relivant.
54711  *
54712  * Fork - LGPL
54713  * <script type="text/javascript">
54714  */
54715  
54716 /**
54717  * @class Roo.LayoutManager
54718  * @extends Roo.util.Observable
54719  * Base class for layout managers.
54720  */
54721 Roo.LayoutManager = function(container, config){
54722     Roo.LayoutManager.superclass.constructor.call(this);
54723     this.el = Roo.get(container);
54724     // ie scrollbar fix
54725     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54726         document.body.scroll = "no";
54727     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54728         this.el.position('relative');
54729     }
54730     this.id = this.el.id;
54731     this.el.addClass("x-layout-container");
54732     /** false to disable window resize monitoring @type Boolean */
54733     this.monitorWindowResize = true;
54734     this.regions = {};
54735     this.addEvents({
54736         /**
54737          * @event layout
54738          * Fires when a layout is performed. 
54739          * @param {Roo.LayoutManager} this
54740          */
54741         "layout" : true,
54742         /**
54743          * @event regionresized
54744          * Fires when the user resizes a region. 
54745          * @param {Roo.LayoutRegion} region The resized region
54746          * @param {Number} newSize The new size (width for east/west, height for north/south)
54747          */
54748         "regionresized" : true,
54749         /**
54750          * @event regioncollapsed
54751          * Fires when a region is collapsed. 
54752          * @param {Roo.LayoutRegion} region The collapsed region
54753          */
54754         "regioncollapsed" : true,
54755         /**
54756          * @event regionexpanded
54757          * Fires when a region is expanded.  
54758          * @param {Roo.LayoutRegion} region The expanded region
54759          */
54760         "regionexpanded" : true
54761     });
54762     this.updating = false;
54763     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54764 };
54765
54766 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54767     /**
54768      * Returns true if this layout is currently being updated
54769      * @return {Boolean}
54770      */
54771     isUpdating : function(){
54772         return this.updating; 
54773     },
54774     
54775     /**
54776      * Suspend the LayoutManager from doing auto-layouts while
54777      * making multiple add or remove calls
54778      */
54779     beginUpdate : function(){
54780         this.updating = true;    
54781     },
54782     
54783     /**
54784      * Restore auto-layouts and optionally disable the manager from performing a layout
54785      * @param {Boolean} noLayout true to disable a layout update 
54786      */
54787     endUpdate : function(noLayout){
54788         this.updating = false;
54789         if(!noLayout){
54790             this.layout();
54791         }    
54792     },
54793     
54794     layout: function(){
54795         
54796     },
54797     
54798     onRegionResized : function(region, newSize){
54799         this.fireEvent("regionresized", region, newSize);
54800         this.layout();
54801     },
54802     
54803     onRegionCollapsed : function(region){
54804         this.fireEvent("regioncollapsed", region);
54805     },
54806     
54807     onRegionExpanded : function(region){
54808         this.fireEvent("regionexpanded", region);
54809     },
54810         
54811     /**
54812      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54813      * performs box-model adjustments.
54814      * @return {Object} The size as an object {width: (the width), height: (the height)}
54815      */
54816     getViewSize : function(){
54817         var size;
54818         if(this.el.dom != document.body){
54819             size = this.el.getSize();
54820         }else{
54821             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54822         }
54823         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54824         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54825         return size;
54826     },
54827     
54828     /**
54829      * Returns the Element this layout is bound to.
54830      * @return {Roo.Element}
54831      */
54832     getEl : function(){
54833         return this.el;
54834     },
54835     
54836     /**
54837      * Returns the specified region.
54838      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54839      * @return {Roo.LayoutRegion}
54840      */
54841     getRegion : function(target){
54842         return this.regions[target.toLowerCase()];
54843     },
54844     
54845     onWindowResize : function(){
54846         if(this.monitorWindowResize){
54847             this.layout();
54848         }
54849     }
54850 });/*
54851  * Based on:
54852  * Ext JS Library 1.1.1
54853  * Copyright(c) 2006-2007, Ext JS, LLC.
54854  *
54855  * Originally Released Under LGPL - original licence link has changed is not relivant.
54856  *
54857  * Fork - LGPL
54858  * <script type="text/javascript">
54859  */
54860 /**
54861  * @class Roo.BorderLayout
54862  * @extends Roo.LayoutManager
54863  * @children Roo.ContentPanel
54864  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54865  * please see: <br><br>
54866  * <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>
54867  * <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>
54868  * Example:
54869  <pre><code>
54870  var layout = new Roo.BorderLayout(document.body, {
54871     north: {
54872         initialSize: 25,
54873         titlebar: false
54874     },
54875     west: {
54876         split:true,
54877         initialSize: 200,
54878         minSize: 175,
54879         maxSize: 400,
54880         titlebar: true,
54881         collapsible: true
54882     },
54883     east: {
54884         split:true,
54885         initialSize: 202,
54886         minSize: 175,
54887         maxSize: 400,
54888         titlebar: true,
54889         collapsible: true
54890     },
54891     south: {
54892         split:true,
54893         initialSize: 100,
54894         minSize: 100,
54895         maxSize: 200,
54896         titlebar: true,
54897         collapsible: true
54898     },
54899     center: {
54900         titlebar: true,
54901         autoScroll:true,
54902         resizeTabs: true,
54903         minTabWidth: 50,
54904         preferredTabWidth: 150
54905     }
54906 });
54907
54908 // shorthand
54909 var CP = Roo.ContentPanel;
54910
54911 layout.beginUpdate();
54912 layout.add("north", new CP("north", "North"));
54913 layout.add("south", new CP("south", {title: "South", closable: true}));
54914 layout.add("west", new CP("west", {title: "West"}));
54915 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54916 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54917 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54918 layout.getRegion("center").showPanel("center1");
54919 layout.endUpdate();
54920 </code></pre>
54921
54922 <b>The container the layout is rendered into can be either the body element or any other element.
54923 If it is not the body element, the container needs to either be an absolute positioned element,
54924 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54925 the container size if it is not the body element.</b>
54926
54927 * @constructor
54928 * Create a new BorderLayout
54929 * @param {String/HTMLElement/Element} container The container this layout is bound to
54930 * @param {Object} config Configuration options
54931  */
54932 Roo.BorderLayout = function(container, config){
54933     config = config || {};
54934     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54935     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54936     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54937         var target = this.factory.validRegions[i];
54938         if(config[target]){
54939             this.addRegion(target, config[target]);
54940         }
54941     }
54942 };
54943
54944 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54945         
54946         /**
54947          * @cfg {Roo.LayoutRegion} east
54948          */
54949         /**
54950          * @cfg {Roo.LayoutRegion} west
54951          */
54952         /**
54953          * @cfg {Roo.LayoutRegion} north
54954          */
54955         /**
54956          * @cfg {Roo.LayoutRegion} south
54957          */
54958         /**
54959          * @cfg {Roo.LayoutRegion} center
54960          */
54961     /**
54962      * Creates and adds a new region if it doesn't already exist.
54963      * @param {String} target The target region key (north, south, east, west or center).
54964      * @param {Object} config The regions config object
54965      * @return {BorderLayoutRegion} The new region
54966      */
54967     addRegion : function(target, config){
54968         if(!this.regions[target]){
54969             var r = this.factory.create(target, this, config);
54970             this.bindRegion(target, r);
54971         }
54972         return this.regions[target];
54973     },
54974
54975     // private (kinda)
54976     bindRegion : function(name, r){
54977         this.regions[name] = r;
54978         r.on("visibilitychange", this.layout, this);
54979         r.on("paneladded", this.layout, this);
54980         r.on("panelremoved", this.layout, this);
54981         r.on("invalidated", this.layout, this);
54982         r.on("resized", this.onRegionResized, this);
54983         r.on("collapsed", this.onRegionCollapsed, this);
54984         r.on("expanded", this.onRegionExpanded, this);
54985     },
54986
54987     /**
54988      * Performs a layout update.
54989      */
54990     layout : function(){
54991         if(this.updating) {
54992             return;
54993         }
54994         var size = this.getViewSize();
54995         var w = size.width;
54996         var h = size.height;
54997         var centerW = w;
54998         var centerH = h;
54999         var centerY = 0;
55000         var centerX = 0;
55001         //var x = 0, y = 0;
55002
55003         var rs = this.regions;
55004         var north = rs["north"];
55005         var south = rs["south"]; 
55006         var west = rs["west"];
55007         var east = rs["east"];
55008         var center = rs["center"];
55009         //if(this.hideOnLayout){ // not supported anymore
55010             //c.el.setStyle("display", "none");
55011         //}
55012         if(north && north.isVisible()){
55013             var b = north.getBox();
55014             var m = north.getMargins();
55015             b.width = w - (m.left+m.right);
55016             b.x = m.left;
55017             b.y = m.top;
55018             centerY = b.height + b.y + m.bottom;
55019             centerH -= centerY;
55020             north.updateBox(this.safeBox(b));
55021         }
55022         if(south && south.isVisible()){
55023             var b = south.getBox();
55024             var m = south.getMargins();
55025             b.width = w - (m.left+m.right);
55026             b.x = m.left;
55027             var totalHeight = (b.height + m.top + m.bottom);
55028             b.y = h - totalHeight + m.top;
55029             centerH -= totalHeight;
55030             south.updateBox(this.safeBox(b));
55031         }
55032         if(west && west.isVisible()){
55033             var b = west.getBox();
55034             var m = west.getMargins();
55035             b.height = centerH - (m.top+m.bottom);
55036             b.x = m.left;
55037             b.y = centerY + m.top;
55038             var totalWidth = (b.width + m.left + m.right);
55039             centerX += totalWidth;
55040             centerW -= totalWidth;
55041             west.updateBox(this.safeBox(b));
55042         }
55043         if(east && east.isVisible()){
55044             var b = east.getBox();
55045             var m = east.getMargins();
55046             b.height = centerH - (m.top+m.bottom);
55047             var totalWidth = (b.width + m.left + m.right);
55048             b.x = w - totalWidth + m.left;
55049             b.y = centerY + m.top;
55050             centerW -= totalWidth;
55051             east.updateBox(this.safeBox(b));
55052         }
55053         if(center){
55054             var m = center.getMargins();
55055             var centerBox = {
55056                 x: centerX + m.left,
55057                 y: centerY + m.top,
55058                 width: centerW - (m.left+m.right),
55059                 height: centerH - (m.top+m.bottom)
55060             };
55061             //if(this.hideOnLayout){
55062                 //center.el.setStyle("display", "block");
55063             //}
55064             center.updateBox(this.safeBox(centerBox));
55065         }
55066         this.el.repaint();
55067         this.fireEvent("layout", this);
55068     },
55069
55070     // private
55071     safeBox : function(box){
55072         box.width = Math.max(0, box.width);
55073         box.height = Math.max(0, box.height);
55074         return box;
55075     },
55076
55077     /**
55078      * Adds a ContentPanel (or subclass) to this layout.
55079      * @param {String} target The target region key (north, south, east, west or center).
55080      * @param {Roo.ContentPanel} panel The panel to add
55081      * @return {Roo.ContentPanel} The added panel
55082      */
55083     add : function(target, panel){
55084          
55085         target = target.toLowerCase();
55086         return this.regions[target].add(panel);
55087     },
55088
55089     /**
55090      * Remove a ContentPanel (or subclass) to this layout.
55091      * @param {String} target The target region key (north, south, east, west or center).
55092      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
55093      * @return {Roo.ContentPanel} The removed panel
55094      */
55095     remove : function(target, panel){
55096         target = target.toLowerCase();
55097         return this.regions[target].remove(panel);
55098     },
55099
55100     /**
55101      * Searches all regions for a panel with the specified id
55102      * @param {String} panelId
55103      * @return {Roo.ContentPanel} The panel or null if it wasn't found
55104      */
55105     findPanel : function(panelId){
55106         var rs = this.regions;
55107         for(var target in rs){
55108             if(typeof rs[target] != "function"){
55109                 var p = rs[target].getPanel(panelId);
55110                 if(p){
55111                     return p;
55112                 }
55113             }
55114         }
55115         return null;
55116     },
55117
55118     /**
55119      * Searches all regions for a panel with the specified id and activates (shows) it.
55120      * @param {String/ContentPanel} panelId The panels id or the panel itself
55121      * @return {Roo.ContentPanel} The shown panel or null
55122      */
55123     showPanel : function(panelId) {
55124       var rs = this.regions;
55125       for(var target in rs){
55126          var r = rs[target];
55127          if(typeof r != "function"){
55128             if(r.hasPanel(panelId)){
55129                return r.showPanel(panelId);
55130             }
55131          }
55132       }
55133       return null;
55134    },
55135
55136    /**
55137      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
55138      * @param {Roo.state.Provider} provider (optional) An alternate state provider
55139      */
55140     restoreState : function(provider){
55141         if(!provider){
55142             provider = Roo.state.Manager;
55143         }
55144         var sm = new Roo.LayoutStateManager();
55145         sm.init(this, provider);
55146     },
55147
55148     /**
55149      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
55150      * object should contain properties for each region to add ContentPanels to, and each property's value should be
55151      * a valid ContentPanel config object.  Example:
55152      * <pre><code>
55153 // Create the main layout
55154 var layout = new Roo.BorderLayout('main-ct', {
55155     west: {
55156         split:true,
55157         minSize: 175,
55158         titlebar: true
55159     },
55160     center: {
55161         title:'Components'
55162     }
55163 }, 'main-ct');
55164
55165 // Create and add multiple ContentPanels at once via configs
55166 layout.batchAdd({
55167    west: {
55168        id: 'source-files',
55169        autoCreate:true,
55170        title:'Ext Source Files',
55171        autoScroll:true,
55172        fitToFrame:true
55173    },
55174    center : {
55175        el: cview,
55176        autoScroll:true,
55177        fitToFrame:true,
55178        toolbar: tb,
55179        resizeEl:'cbody'
55180    }
55181 });
55182 </code></pre>
55183      * @param {Object} regions An object containing ContentPanel configs by region name
55184      */
55185     batchAdd : function(regions){
55186         this.beginUpdate();
55187         for(var rname in regions){
55188             var lr = this.regions[rname];
55189             if(lr){
55190                 this.addTypedPanels(lr, regions[rname]);
55191             }
55192         }
55193         this.endUpdate();
55194     },
55195
55196     // private
55197     addTypedPanels : function(lr, ps){
55198         if(typeof ps == 'string'){
55199             lr.add(new Roo.ContentPanel(ps));
55200         }
55201         else if(ps instanceof Array){
55202             for(var i =0, len = ps.length; i < len; i++){
55203                 this.addTypedPanels(lr, ps[i]);
55204             }
55205         }
55206         else if(!ps.events){ // raw config?
55207             var el = ps.el;
55208             delete ps.el; // prevent conflict
55209             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
55210         }
55211         else {  // panel object assumed!
55212             lr.add(ps);
55213         }
55214     },
55215     /**
55216      * Adds a xtype elements to the layout.
55217      * <pre><code>
55218
55219 layout.addxtype({
55220        xtype : 'ContentPanel',
55221        region: 'west',
55222        items: [ .... ]
55223    }
55224 );
55225
55226 layout.addxtype({
55227         xtype : 'NestedLayoutPanel',
55228         region: 'west',
55229         layout: {
55230            center: { },
55231            west: { }   
55232         },
55233         items : [ ... list of content panels or nested layout panels.. ]
55234    }
55235 );
55236 </code></pre>
55237      * @param {Object} cfg Xtype definition of item to add.
55238      */
55239     addxtype : function(cfg)
55240     {
55241         // basically accepts a pannel...
55242         // can accept a layout region..!?!?
55243         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
55244         
55245         if (!cfg.xtype.match(/Panel$/)) {
55246             return false;
55247         }
55248         var ret = false;
55249         
55250         if (typeof(cfg.region) == 'undefined') {
55251             Roo.log("Failed to add Panel, region was not set");
55252             Roo.log(cfg);
55253             return false;
55254         }
55255         var region = cfg.region;
55256         delete cfg.region;
55257         
55258           
55259         var xitems = [];
55260         if (cfg.items) {
55261             xitems = cfg.items;
55262             delete cfg.items;
55263         }
55264         var nb = false;
55265         
55266         switch(cfg.xtype) 
55267         {
55268             case 'ContentPanel':  // ContentPanel (el, cfg)
55269             case 'ScrollPanel':  // ContentPanel (el, cfg)
55270             case 'ViewPanel': 
55271                 if(cfg.autoCreate) {
55272                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55273                 } else {
55274                     var el = this.el.createChild();
55275                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
55276                 }
55277                 
55278                 this.add(region, ret);
55279                 break;
55280             
55281             
55282             case 'TreePanel': // our new panel!
55283                 cfg.el = this.el.createChild();
55284                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55285                 this.add(region, ret);
55286                 break;
55287             
55288             case 'NestedLayoutPanel': 
55289                 // create a new Layout (which is  a Border Layout...
55290                 var el = this.el.createChild();
55291                 var clayout = cfg.layout;
55292                 delete cfg.layout;
55293                 clayout.items   = clayout.items  || [];
55294                 // replace this exitems with the clayout ones..
55295                 xitems = clayout.items;
55296                  
55297                 
55298                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
55299                     cfg.background = false;
55300                 }
55301                 var layout = new Roo.BorderLayout(el, clayout);
55302                 
55303                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
55304                 //console.log('adding nested layout panel '  + cfg.toSource());
55305                 this.add(region, ret);
55306                 nb = {}; /// find first...
55307                 break;
55308                 
55309             case 'GridPanel': 
55310             
55311                 // needs grid and region
55312                 
55313                 //var el = this.getRegion(region).el.createChild();
55314                 var el = this.el.createChild();
55315                 // create the grid first...
55316                 
55317                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
55318                 delete cfg.grid;
55319                 if (region == 'center' && this.active ) {
55320                     cfg.background = false;
55321                 }
55322                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
55323                 
55324                 this.add(region, ret);
55325                 if (cfg.background) {
55326                     ret.on('activate', function(gp) {
55327                         if (!gp.grid.rendered) {
55328                             gp.grid.render();
55329                         }
55330                     });
55331                 } else {
55332                     grid.render();
55333                 }
55334                 break;
55335            
55336            
55337            
55338                 
55339                 
55340                 
55341             default:
55342                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
55343                     
55344                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
55345                     this.add(region, ret);
55346                 } else {
55347                 
55348                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
55349                     return null;
55350                 }
55351                 
55352              // GridPanel (grid, cfg)
55353             
55354         }
55355         this.beginUpdate();
55356         // add children..
55357         var region = '';
55358         var abn = {};
55359         Roo.each(xitems, function(i)  {
55360             region = nb && i.region ? i.region : false;
55361             
55362             var add = ret.addxtype(i);
55363            
55364             if (region) {
55365                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
55366                 if (!i.background) {
55367                     abn[region] = nb[region] ;
55368                 }
55369             }
55370             
55371         });
55372         this.endUpdate();
55373
55374         // make the last non-background panel active..
55375         //if (nb) { Roo.log(abn); }
55376         if (nb) {
55377             
55378             for(var r in abn) {
55379                 region = this.getRegion(r);
55380                 if (region) {
55381                     // tried using nb[r], but it does not work..
55382                      
55383                     region.showPanel(abn[r]);
55384                    
55385                 }
55386             }
55387         }
55388         return ret;
55389         
55390     }
55391 });
55392
55393 /**
55394  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55395  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55396  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55397  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55398  * <pre><code>
55399 // shorthand
55400 var CP = Roo.ContentPanel;
55401
55402 var layout = Roo.BorderLayout.create({
55403     north: {
55404         initialSize: 25,
55405         titlebar: false,
55406         panels: [new CP("north", "North")]
55407     },
55408     west: {
55409         split:true,
55410         initialSize: 200,
55411         minSize: 175,
55412         maxSize: 400,
55413         titlebar: true,
55414         collapsible: true,
55415         panels: [new CP("west", {title: "West"})]
55416     },
55417     east: {
55418         split:true,
55419         initialSize: 202,
55420         minSize: 175,
55421         maxSize: 400,
55422         titlebar: true,
55423         collapsible: true,
55424         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55425     },
55426     south: {
55427         split:true,
55428         initialSize: 100,
55429         minSize: 100,
55430         maxSize: 200,
55431         titlebar: true,
55432         collapsible: true,
55433         panels: [new CP("south", {title: "South", closable: true})]
55434     },
55435     center: {
55436         titlebar: true,
55437         autoScroll:true,
55438         resizeTabs: true,
55439         minTabWidth: 50,
55440         preferredTabWidth: 150,
55441         panels: [
55442             new CP("center1", {title: "Close Me", closable: true}),
55443             new CP("center2", {title: "Center Panel", closable: false})
55444         ]
55445     }
55446 }, document.body);
55447
55448 layout.getRegion("center").showPanel("center1");
55449 </code></pre>
55450  * @param config
55451  * @param targetEl
55452  */
55453 Roo.BorderLayout.create = function(config, targetEl){
55454     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55455     layout.beginUpdate();
55456     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55457     for(var j = 0, jlen = regions.length; j < jlen; j++){
55458         var lr = regions[j];
55459         if(layout.regions[lr] && config[lr].panels){
55460             var r = layout.regions[lr];
55461             var ps = config[lr].panels;
55462             layout.addTypedPanels(r, ps);
55463         }
55464     }
55465     layout.endUpdate();
55466     return layout;
55467 };
55468
55469 // private
55470 Roo.BorderLayout.RegionFactory = {
55471     // private
55472     validRegions : ["north","south","east","west","center"],
55473
55474     // private
55475     create : function(target, mgr, config){
55476         target = target.toLowerCase();
55477         if(config.lightweight || config.basic){
55478             return new Roo.BasicLayoutRegion(mgr, config, target);
55479         }
55480         switch(target){
55481             case "north":
55482                 return new Roo.NorthLayoutRegion(mgr, config);
55483             case "south":
55484                 return new Roo.SouthLayoutRegion(mgr, config);
55485             case "east":
55486                 return new Roo.EastLayoutRegion(mgr, config);
55487             case "west":
55488                 return new Roo.WestLayoutRegion(mgr, config);
55489             case "center":
55490                 return new Roo.CenterLayoutRegion(mgr, config);
55491         }
55492         throw 'Layout region "'+target+'" not supported.';
55493     }
55494 };/*
55495  * Based on:
55496  * Ext JS Library 1.1.1
55497  * Copyright(c) 2006-2007, Ext JS, LLC.
55498  *
55499  * Originally Released Under LGPL - original licence link has changed is not relivant.
55500  *
55501  * Fork - LGPL
55502  * <script type="text/javascript">
55503  */
55504  
55505 /**
55506  * @class Roo.BasicLayoutRegion
55507  * @extends Roo.util.Observable
55508  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55509  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55510  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55511  */
55512 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55513     this.mgr = mgr;
55514     this.position  = pos;
55515     this.events = {
55516         /**
55517          * @scope Roo.BasicLayoutRegion
55518          */
55519         
55520         /**
55521          * @event beforeremove
55522          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55523          * @param {Roo.LayoutRegion} this
55524          * @param {Roo.ContentPanel} panel The panel
55525          * @param {Object} e The cancel event object
55526          */
55527         "beforeremove" : true,
55528         /**
55529          * @event invalidated
55530          * Fires when the layout for this region is changed.
55531          * @param {Roo.LayoutRegion} this
55532          */
55533         "invalidated" : true,
55534         /**
55535          * @event visibilitychange
55536          * Fires when this region is shown or hidden 
55537          * @param {Roo.LayoutRegion} this
55538          * @param {Boolean} visibility true or false
55539          */
55540         "visibilitychange" : true,
55541         /**
55542          * @event paneladded
55543          * Fires when a panel is added. 
55544          * @param {Roo.LayoutRegion} this
55545          * @param {Roo.ContentPanel} panel The panel
55546          */
55547         "paneladded" : true,
55548         /**
55549          * @event panelremoved
55550          * Fires when a panel is removed. 
55551          * @param {Roo.LayoutRegion} this
55552          * @param {Roo.ContentPanel} panel The panel
55553          */
55554         "panelremoved" : true,
55555         /**
55556          * @event beforecollapse
55557          * Fires when this region before collapse.
55558          * @param {Roo.LayoutRegion} this
55559          */
55560         "beforecollapse" : true,
55561         /**
55562          * @event collapsed
55563          * Fires when this region is collapsed.
55564          * @param {Roo.LayoutRegion} this
55565          */
55566         "collapsed" : true,
55567         /**
55568          * @event expanded
55569          * Fires when this region is expanded.
55570          * @param {Roo.LayoutRegion} this
55571          */
55572         "expanded" : true,
55573         /**
55574          * @event slideshow
55575          * Fires when this region is slid into view.
55576          * @param {Roo.LayoutRegion} this
55577          */
55578         "slideshow" : true,
55579         /**
55580          * @event slidehide
55581          * Fires when this region slides out of view. 
55582          * @param {Roo.LayoutRegion} this
55583          */
55584         "slidehide" : true,
55585         /**
55586          * @event panelactivated
55587          * Fires when a panel is activated. 
55588          * @param {Roo.LayoutRegion} this
55589          * @param {Roo.ContentPanel} panel The activated panel
55590          */
55591         "panelactivated" : true,
55592         /**
55593          * @event resized
55594          * Fires when the user resizes this region. 
55595          * @param {Roo.LayoutRegion} this
55596          * @param {Number} newSize The new size (width for east/west, height for north/south)
55597          */
55598         "resized" : true
55599     };
55600     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55601     this.panels = new Roo.util.MixedCollection();
55602     this.panels.getKey = this.getPanelId.createDelegate(this);
55603     this.box = null;
55604     this.activePanel = null;
55605     // ensure listeners are added...
55606     
55607     if (config.listeners || config.events) {
55608         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55609             listeners : config.listeners || {},
55610             events : config.events || {}
55611         });
55612     }
55613     
55614     if(skipConfig !== true){
55615         this.applyConfig(config);
55616     }
55617 };
55618
55619 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55620     getPanelId : function(p){
55621         return p.getId();
55622     },
55623     
55624     applyConfig : function(config){
55625         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55626         this.config = config;
55627         
55628     },
55629     
55630     /**
55631      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55632      * the width, for horizontal (north, south) the height.
55633      * @param {Number} newSize The new width or height
55634      */
55635     resizeTo : function(newSize){
55636         var el = this.el ? this.el :
55637                  (this.activePanel ? this.activePanel.getEl() : null);
55638         if(el){
55639             switch(this.position){
55640                 case "east":
55641                 case "west":
55642                     el.setWidth(newSize);
55643                     this.fireEvent("resized", this, newSize);
55644                 break;
55645                 case "north":
55646                 case "south":
55647                     el.setHeight(newSize);
55648                     this.fireEvent("resized", this, newSize);
55649                 break;                
55650             }
55651         }
55652     },
55653     
55654     getBox : function(){
55655         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55656     },
55657     
55658     getMargins : function(){
55659         return this.margins;
55660     },
55661     
55662     updateBox : function(box){
55663         this.box = box;
55664         var el = this.activePanel.getEl();
55665         el.dom.style.left = box.x + "px";
55666         el.dom.style.top = box.y + "px";
55667         this.activePanel.setSize(box.width, box.height);
55668     },
55669     
55670     /**
55671      * Returns the container element for this region.
55672      * @return {Roo.Element}
55673      */
55674     getEl : function(){
55675         return this.activePanel;
55676     },
55677     
55678     /**
55679      * Returns true if this region is currently visible.
55680      * @return {Boolean}
55681      */
55682     isVisible : function(){
55683         return this.activePanel ? true : false;
55684     },
55685     
55686     setActivePanel : function(panel){
55687         panel = this.getPanel(panel);
55688         if(this.activePanel && this.activePanel != panel){
55689             this.activePanel.setActiveState(false);
55690             this.activePanel.getEl().setLeftTop(-10000,-10000);
55691         }
55692         this.activePanel = panel;
55693         panel.setActiveState(true);
55694         if(this.box){
55695             panel.setSize(this.box.width, this.box.height);
55696         }
55697         this.fireEvent("panelactivated", this, panel);
55698         this.fireEvent("invalidated");
55699     },
55700     
55701     /**
55702      * Show the specified panel.
55703      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55704      * @return {Roo.ContentPanel} The shown panel or null
55705      */
55706     showPanel : function(panel){
55707         if(panel = this.getPanel(panel)){
55708             this.setActivePanel(panel);
55709         }
55710         return panel;
55711     },
55712     
55713     /**
55714      * Get the active panel for this region.
55715      * @return {Roo.ContentPanel} The active panel or null
55716      */
55717     getActivePanel : function(){
55718         return this.activePanel;
55719     },
55720     
55721     /**
55722      * Add the passed ContentPanel(s)
55723      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55724      * @return {Roo.ContentPanel} The panel added (if only one was added)
55725      */
55726     add : function(panel){
55727         if(arguments.length > 1){
55728             for(var i = 0, len = arguments.length; i < len; i++) {
55729                 this.add(arguments[i]);
55730             }
55731             return null;
55732         }
55733         if(this.hasPanel(panel)){
55734             this.showPanel(panel);
55735             return panel;
55736         }
55737         var el = panel.getEl();
55738         if(el.dom.parentNode != this.mgr.el.dom){
55739             this.mgr.el.dom.appendChild(el.dom);
55740         }
55741         if(panel.setRegion){
55742             panel.setRegion(this);
55743         }
55744         this.panels.add(panel);
55745         el.setStyle("position", "absolute");
55746         if(!panel.background){
55747             this.setActivePanel(panel);
55748             if(this.config.initialSize && this.panels.getCount()==1){
55749                 this.resizeTo(this.config.initialSize);
55750             }
55751         }
55752         this.fireEvent("paneladded", this, panel);
55753         return panel;
55754     },
55755     
55756     /**
55757      * Returns true if the panel is in this region.
55758      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55759      * @return {Boolean}
55760      */
55761     hasPanel : function(panel){
55762         if(typeof panel == "object"){ // must be panel obj
55763             panel = panel.getId();
55764         }
55765         return this.getPanel(panel) ? true : false;
55766     },
55767     
55768     /**
55769      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55770      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55771      * @param {Boolean} preservePanel Overrides the config preservePanel option
55772      * @return {Roo.ContentPanel} The panel that was removed
55773      */
55774     remove : function(panel, preservePanel){
55775         panel = this.getPanel(panel);
55776         if(!panel){
55777             return null;
55778         }
55779         var e = {};
55780         this.fireEvent("beforeremove", this, panel, e);
55781         if(e.cancel === true){
55782             return null;
55783         }
55784         var panelId = panel.getId();
55785         this.panels.removeKey(panelId);
55786         return panel;
55787     },
55788     
55789     /**
55790      * Returns the panel specified or null if it's not in this region.
55791      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55792      * @return {Roo.ContentPanel}
55793      */
55794     getPanel : function(id){
55795         if(typeof id == "object"){ // must be panel obj
55796             return id;
55797         }
55798         return this.panels.get(id);
55799     },
55800     
55801     /**
55802      * Returns this regions position (north/south/east/west/center).
55803      * @return {String} 
55804      */
55805     getPosition: function(){
55806         return this.position;    
55807     }
55808 });/*
55809  * Based on:
55810  * Ext JS Library 1.1.1
55811  * Copyright(c) 2006-2007, Ext JS, LLC.
55812  *
55813  * Originally Released Under LGPL - original licence link has changed is not relivant.
55814  *
55815  * Fork - LGPL
55816  * <script type="text/javascript">
55817  */
55818  
55819 /**
55820  * @class Roo.LayoutRegion
55821  * @extends Roo.BasicLayoutRegion
55822  * This class represents a region in a layout manager.
55823  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55824  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55825  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55826  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55827  * @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})
55828  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55829  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55830  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55831  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55832  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55833  * @cfg {String}    title           The title for the region (overrides panel titles)
55834  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55835  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55836  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55837  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55838  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55839  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55840  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55841  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55842  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55843  * @cfg {Boolean}   showPin         True to show a pin button
55844  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55845  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55846  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55847  * @cfg {Number}    width           For East/West panels
55848  * @cfg {Number}    height          For North/South panels
55849  * @cfg {Boolean}   split           To show the splitter
55850  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55851  */
55852 Roo.LayoutRegion = function(mgr, config, pos){
55853     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55854     var dh = Roo.DomHelper;
55855     /** This region's container element 
55856     * @type Roo.Element */
55857     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55858     /** This region's title element 
55859     * @type Roo.Element */
55860
55861     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55862         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55863         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55864     ]}, true);
55865     this.titleEl.enableDisplayMode();
55866     /** This region's title text element 
55867     * @type HTMLElement */
55868     this.titleTextEl = this.titleEl.dom.firstChild;
55869     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55870     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55871     this.closeBtn.enableDisplayMode();
55872     this.closeBtn.on("click", this.closeClicked, this);
55873     this.closeBtn.hide();
55874
55875     this.createBody(config);
55876     this.visible = true;
55877     this.collapsed = false;
55878
55879     if(config.hideWhenEmpty){
55880         this.hide();
55881         this.on("paneladded", this.validateVisibility, this);
55882         this.on("panelremoved", this.validateVisibility, this);
55883     }
55884     this.applyConfig(config);
55885 };
55886
55887 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55888
55889     createBody : function(){
55890         /** This region's body element 
55891         * @type Roo.Element */
55892         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55893     },
55894
55895     applyConfig : function(c){
55896         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55897             var dh = Roo.DomHelper;
55898             if(c.titlebar !== false){
55899                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55900                 this.collapseBtn.on("click", this.collapse, this);
55901                 this.collapseBtn.enableDisplayMode();
55902
55903                 if(c.showPin === true || this.showPin){
55904                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55905                     this.stickBtn.enableDisplayMode();
55906                     this.stickBtn.on("click", this.expand, this);
55907                     this.stickBtn.hide();
55908                 }
55909             }
55910             /** This region's collapsed element
55911             * @type Roo.Element */
55912             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55913                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55914             ]}, true);
55915             if(c.floatable !== false){
55916                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55917                this.collapsedEl.on("click", this.collapseClick, this);
55918             }
55919
55920             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55921                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55922                    id: "message", unselectable: "on", style:{"float":"left"}});
55923                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55924              }
55925             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55926             this.expandBtn.on("click", this.expand, this);
55927         }
55928         if(this.collapseBtn){
55929             this.collapseBtn.setVisible(c.collapsible == true);
55930         }
55931         this.cmargins = c.cmargins || this.cmargins ||
55932                          (this.position == "west" || this.position == "east" ?
55933                              {top: 0, left: 2, right:2, bottom: 0} :
55934                              {top: 2, left: 0, right:0, bottom: 2});
55935         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55936         this.bottomTabs = c.tabPosition != "top";
55937         this.autoScroll = c.autoScroll || false;
55938         if(this.autoScroll){
55939             this.bodyEl.setStyle("overflow", "auto");
55940         }else{
55941             this.bodyEl.setStyle("overflow", "hidden");
55942         }
55943         //if(c.titlebar !== false){
55944             if((!c.titlebar && !c.title) || c.titlebar === false){
55945                 this.titleEl.hide();
55946             }else{
55947                 this.titleEl.show();
55948                 if(c.title){
55949                     this.titleTextEl.innerHTML = c.title;
55950                 }
55951             }
55952         //}
55953         this.duration = c.duration || .30;
55954         this.slideDuration = c.slideDuration || .45;
55955         this.config = c;
55956         if(c.collapsed){
55957             this.collapse(true);
55958         }
55959         if(c.hidden){
55960             this.hide();
55961         }
55962     },
55963     /**
55964      * Returns true if this region is currently visible.
55965      * @return {Boolean}
55966      */
55967     isVisible : function(){
55968         return this.visible;
55969     },
55970
55971     /**
55972      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55973      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55974      */
55975     setCollapsedTitle : function(title){
55976         title = title || "&#160;";
55977         if(this.collapsedTitleTextEl){
55978             this.collapsedTitleTextEl.innerHTML = title;
55979         }
55980     },
55981
55982     getBox : function(){
55983         var b;
55984         if(!this.collapsed){
55985             b = this.el.getBox(false, true);
55986         }else{
55987             b = this.collapsedEl.getBox(false, true);
55988         }
55989         return b;
55990     },
55991
55992     getMargins : function(){
55993         return this.collapsed ? this.cmargins : this.margins;
55994     },
55995
55996     highlight : function(){
55997         this.el.addClass("x-layout-panel-dragover");
55998     },
55999
56000     unhighlight : function(){
56001         this.el.removeClass("x-layout-panel-dragover");
56002     },
56003
56004     updateBox : function(box){
56005         this.box = box;
56006         if(!this.collapsed){
56007             this.el.dom.style.left = box.x + "px";
56008             this.el.dom.style.top = box.y + "px";
56009             this.updateBody(box.width, box.height);
56010         }else{
56011             this.collapsedEl.dom.style.left = box.x + "px";
56012             this.collapsedEl.dom.style.top = box.y + "px";
56013             this.collapsedEl.setSize(box.width, box.height);
56014         }
56015         if(this.tabs){
56016             this.tabs.autoSizeTabs();
56017         }
56018     },
56019
56020     updateBody : function(w, h){
56021         if(w !== null){
56022             this.el.setWidth(w);
56023             w -= this.el.getBorderWidth("rl");
56024             if(this.config.adjustments){
56025                 w += this.config.adjustments[0];
56026             }
56027         }
56028         if(h !== null){
56029             this.el.setHeight(h);
56030             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
56031             h -= this.el.getBorderWidth("tb");
56032             if(this.config.adjustments){
56033                 h += this.config.adjustments[1];
56034             }
56035             this.bodyEl.setHeight(h);
56036             if(this.tabs){
56037                 h = this.tabs.syncHeight(h);
56038             }
56039         }
56040         if(this.panelSize){
56041             w = w !== null ? w : this.panelSize.width;
56042             h = h !== null ? h : this.panelSize.height;
56043         }
56044         if(this.activePanel){
56045             var el = this.activePanel.getEl();
56046             w = w !== null ? w : el.getWidth();
56047             h = h !== null ? h : el.getHeight();
56048             this.panelSize = {width: w, height: h};
56049             this.activePanel.setSize(w, h);
56050         }
56051         if(Roo.isIE && this.tabs){
56052             this.tabs.el.repaint();
56053         }
56054     },
56055
56056     /**
56057      * Returns the container element for this region.
56058      * @return {Roo.Element}
56059      */
56060     getEl : function(){
56061         return this.el;
56062     },
56063
56064     /**
56065      * Hides this region.
56066      */
56067     hide : function(){
56068         if(!this.collapsed){
56069             this.el.dom.style.left = "-2000px";
56070             this.el.hide();
56071         }else{
56072             this.collapsedEl.dom.style.left = "-2000px";
56073             this.collapsedEl.hide();
56074         }
56075         this.visible = false;
56076         this.fireEvent("visibilitychange", this, false);
56077     },
56078
56079     /**
56080      * Shows this region if it was previously hidden.
56081      */
56082     show : function(){
56083         if(!this.collapsed){
56084             this.el.show();
56085         }else{
56086             this.collapsedEl.show();
56087         }
56088         this.visible = true;
56089         this.fireEvent("visibilitychange", this, true);
56090     },
56091
56092     closeClicked : function(){
56093         if(this.activePanel){
56094             this.remove(this.activePanel);
56095         }
56096     },
56097
56098     collapseClick : function(e){
56099         if(this.isSlid){
56100            e.stopPropagation();
56101            this.slideIn();
56102         }else{
56103            e.stopPropagation();
56104            this.slideOut();
56105         }
56106     },
56107
56108     /**
56109      * Collapses this region.
56110      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
56111      */
56112     collapse : function(skipAnim, skipCheck){
56113         if(this.collapsed) {
56114             return;
56115         }
56116         
56117         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
56118             
56119             this.collapsed = true;
56120             if(this.split){
56121                 this.split.el.hide();
56122             }
56123             if(this.config.animate && skipAnim !== true){
56124                 this.fireEvent("invalidated", this);
56125                 this.animateCollapse();
56126             }else{
56127                 this.el.setLocation(-20000,-20000);
56128                 this.el.hide();
56129                 this.collapsedEl.show();
56130                 this.fireEvent("collapsed", this);
56131                 this.fireEvent("invalidated", this);
56132             }
56133         }
56134         
56135     },
56136
56137     animateCollapse : function(){
56138         // overridden
56139     },
56140
56141     /**
56142      * Expands this region if it was previously collapsed.
56143      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
56144      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
56145      */
56146     expand : function(e, skipAnim){
56147         if(e) {
56148             e.stopPropagation();
56149         }
56150         if(!this.collapsed || this.el.hasActiveFx()) {
56151             return;
56152         }
56153         if(this.isSlid){
56154             this.afterSlideIn();
56155             skipAnim = true;
56156         }
56157         this.collapsed = false;
56158         if(this.config.animate && skipAnim !== true){
56159             this.animateExpand();
56160         }else{
56161             this.el.show();
56162             if(this.split){
56163                 this.split.el.show();
56164             }
56165             this.collapsedEl.setLocation(-2000,-2000);
56166             this.collapsedEl.hide();
56167             this.fireEvent("invalidated", this);
56168             this.fireEvent("expanded", this);
56169         }
56170     },
56171
56172     animateExpand : function(){
56173         // overridden
56174     },
56175
56176     initTabs : function()
56177     {
56178         this.bodyEl.setStyle("overflow", "hidden");
56179         var ts = new Roo.TabPanel(
56180                 this.bodyEl.dom,
56181                 {
56182                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
56183                     disableTooltips: this.config.disableTabTips,
56184                     toolbar : this.config.toolbar
56185                 }
56186         );
56187         if(this.config.hideTabs){
56188             ts.stripWrap.setDisplayed(false);
56189         }
56190         this.tabs = ts;
56191         ts.resizeTabs = this.config.resizeTabs === true;
56192         ts.minTabWidth = this.config.minTabWidth || 40;
56193         ts.maxTabWidth = this.config.maxTabWidth || 250;
56194         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
56195         ts.monitorResize = false;
56196         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56197         ts.bodyEl.addClass('x-layout-tabs-body');
56198         this.panels.each(this.initPanelAsTab, this);
56199     },
56200
56201     initPanelAsTab : function(panel){
56202         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
56203                     this.config.closeOnTab && panel.isClosable());
56204         if(panel.tabTip !== undefined){
56205             ti.setTooltip(panel.tabTip);
56206         }
56207         ti.on("activate", function(){
56208               this.setActivePanel(panel);
56209         }, this);
56210         if(this.config.closeOnTab){
56211             ti.on("beforeclose", function(t, e){
56212                 e.cancel = true;
56213                 this.remove(panel);
56214             }, this);
56215         }
56216         return ti;
56217     },
56218
56219     updatePanelTitle : function(panel, title){
56220         if(this.activePanel == panel){
56221             this.updateTitle(title);
56222         }
56223         if(this.tabs){
56224             var ti = this.tabs.getTab(panel.getEl().id);
56225             ti.setText(title);
56226             if(panel.tabTip !== undefined){
56227                 ti.setTooltip(panel.tabTip);
56228             }
56229         }
56230     },
56231
56232     updateTitle : function(title){
56233         if(this.titleTextEl && !this.config.title){
56234             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
56235         }
56236     },
56237
56238     setActivePanel : function(panel){
56239         panel = this.getPanel(panel);
56240         if(this.activePanel && this.activePanel != panel){
56241             this.activePanel.setActiveState(false);
56242         }
56243         this.activePanel = panel;
56244         panel.setActiveState(true);
56245         if(this.panelSize){
56246             panel.setSize(this.panelSize.width, this.panelSize.height);
56247         }
56248         if(this.closeBtn){
56249             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
56250         }
56251         this.updateTitle(panel.getTitle());
56252         if(this.tabs){
56253             this.fireEvent("invalidated", this);
56254         }
56255         this.fireEvent("panelactivated", this, panel);
56256     },
56257
56258     /**
56259      * Shows the specified panel.
56260      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
56261      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
56262      */
56263     showPanel : function(panel)
56264     {
56265         panel = this.getPanel(panel);
56266         if(panel){
56267             if(this.tabs){
56268                 var tab = this.tabs.getTab(panel.getEl().id);
56269                 if(tab.isHidden()){
56270                     this.tabs.unhideTab(tab.id);
56271                 }
56272                 tab.activate();
56273             }else{
56274                 this.setActivePanel(panel);
56275             }
56276         }
56277         return panel;
56278     },
56279
56280     /**
56281      * Get the active panel for this region.
56282      * @return {Roo.ContentPanel} The active panel or null
56283      */
56284     getActivePanel : function(){
56285         return this.activePanel;
56286     },
56287
56288     validateVisibility : function(){
56289         if(this.panels.getCount() < 1){
56290             this.updateTitle("&#160;");
56291             this.closeBtn.hide();
56292             this.hide();
56293         }else{
56294             if(!this.isVisible()){
56295                 this.show();
56296             }
56297         }
56298     },
56299
56300     /**
56301      * Adds the passed ContentPanel(s) to this region.
56302      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
56303      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
56304      */
56305     add : function(panel){
56306         if(arguments.length > 1){
56307             for(var i = 0, len = arguments.length; i < len; i++) {
56308                 this.add(arguments[i]);
56309             }
56310             return null;
56311         }
56312         if(this.hasPanel(panel)){
56313             this.showPanel(panel);
56314             return panel;
56315         }
56316         panel.setRegion(this);
56317         this.panels.add(panel);
56318         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
56319             this.bodyEl.dom.appendChild(panel.getEl().dom);
56320             if(panel.background !== true){
56321                 this.setActivePanel(panel);
56322             }
56323             this.fireEvent("paneladded", this, panel);
56324             return panel;
56325         }
56326         if(!this.tabs){
56327             this.initTabs();
56328         }else{
56329             this.initPanelAsTab(panel);
56330         }
56331         if(panel.background !== true){
56332             this.tabs.activate(panel.getEl().id);
56333         }
56334         this.fireEvent("paneladded", this, panel);
56335         return panel;
56336     },
56337
56338     /**
56339      * Hides the tab for the specified panel.
56340      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56341      */
56342     hidePanel : function(panel){
56343         if(this.tabs && (panel = this.getPanel(panel))){
56344             this.tabs.hideTab(panel.getEl().id);
56345         }
56346     },
56347
56348     /**
56349      * Unhides the tab for a previously hidden panel.
56350      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56351      */
56352     unhidePanel : function(panel){
56353         if(this.tabs && (panel = this.getPanel(panel))){
56354             this.tabs.unhideTab(panel.getEl().id);
56355         }
56356     },
56357
56358     clearPanels : function(){
56359         while(this.panels.getCount() > 0){
56360              this.remove(this.panels.first());
56361         }
56362     },
56363
56364     /**
56365      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
56366      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
56367      * @param {Boolean} preservePanel Overrides the config preservePanel option
56368      * @return {Roo.ContentPanel} The panel that was removed
56369      */
56370     remove : function(panel, preservePanel){
56371         panel = this.getPanel(panel);
56372         if(!panel){
56373             return null;
56374         }
56375         var e = {};
56376         this.fireEvent("beforeremove", this, panel, e);
56377         if(e.cancel === true){
56378             return null;
56379         }
56380         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56381         var panelId = panel.getId();
56382         this.panels.removeKey(panelId);
56383         if(preservePanel){
56384             document.body.appendChild(panel.getEl().dom);
56385         }
56386         if(this.tabs){
56387             this.tabs.removeTab(panel.getEl().id);
56388         }else if (!preservePanel){
56389             this.bodyEl.dom.removeChild(panel.getEl().dom);
56390         }
56391         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56392             var p = this.panels.first();
56393             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56394             tempEl.appendChild(p.getEl().dom);
56395             this.bodyEl.update("");
56396             this.bodyEl.dom.appendChild(p.getEl().dom);
56397             tempEl = null;
56398             this.updateTitle(p.getTitle());
56399             this.tabs = null;
56400             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56401             this.setActivePanel(p);
56402         }
56403         panel.setRegion(null);
56404         if(this.activePanel == panel){
56405             this.activePanel = null;
56406         }
56407         if(this.config.autoDestroy !== false && preservePanel !== true){
56408             try{panel.destroy();}catch(e){}
56409         }
56410         this.fireEvent("panelremoved", this, panel);
56411         return panel;
56412     },
56413
56414     /**
56415      * Returns the TabPanel component used by this region
56416      * @return {Roo.TabPanel}
56417      */
56418     getTabs : function(){
56419         return this.tabs;
56420     },
56421
56422     createTool : function(parentEl, className){
56423         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56424             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56425         btn.addClassOnOver("x-layout-tools-button-over");
56426         return btn;
56427     }
56428 });/*
56429  * Based on:
56430  * Ext JS Library 1.1.1
56431  * Copyright(c) 2006-2007, Ext JS, LLC.
56432  *
56433  * Originally Released Under LGPL - original licence link has changed is not relivant.
56434  *
56435  * Fork - LGPL
56436  * <script type="text/javascript">
56437  */
56438  
56439
56440
56441 /**
56442  * @class Roo.SplitLayoutRegion
56443  * @extends Roo.LayoutRegion
56444  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56445  */
56446 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56447     this.cursor = cursor;
56448     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56449 };
56450
56451 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56452     splitTip : "Drag to resize.",
56453     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56454     useSplitTips : false,
56455
56456     applyConfig : function(config){
56457         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56458         if(config.split){
56459             if(!this.split){
56460                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56461                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56462                 /** The SplitBar for this region 
56463                 * @type Roo.SplitBar */
56464                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56465                 this.split.on("moved", this.onSplitMove, this);
56466                 this.split.useShim = config.useShim === true;
56467                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56468                 if(this.useSplitTips){
56469                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56470                 }
56471                 if(config.collapsible){
56472                     this.split.el.on("dblclick", this.collapse,  this);
56473                 }
56474             }
56475             if(typeof config.minSize != "undefined"){
56476                 this.split.minSize = config.minSize;
56477             }
56478             if(typeof config.maxSize != "undefined"){
56479                 this.split.maxSize = config.maxSize;
56480             }
56481             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56482                 this.hideSplitter();
56483             }
56484         }
56485     },
56486
56487     getHMaxSize : function(){
56488          var cmax = this.config.maxSize || 10000;
56489          var center = this.mgr.getRegion("center");
56490          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56491     },
56492
56493     getVMaxSize : function(){
56494          var cmax = this.config.maxSize || 10000;
56495          var center = this.mgr.getRegion("center");
56496          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56497     },
56498
56499     onSplitMove : function(split, newSize){
56500         this.fireEvent("resized", this, newSize);
56501     },
56502     
56503     /** 
56504      * Returns the {@link Roo.SplitBar} for this region.
56505      * @return {Roo.SplitBar}
56506      */
56507     getSplitBar : function(){
56508         return this.split;
56509     },
56510     
56511     hide : function(){
56512         this.hideSplitter();
56513         Roo.SplitLayoutRegion.superclass.hide.call(this);
56514     },
56515
56516     hideSplitter : function(){
56517         if(this.split){
56518             this.split.el.setLocation(-2000,-2000);
56519             this.split.el.hide();
56520         }
56521     },
56522
56523     show : function(){
56524         if(this.split){
56525             this.split.el.show();
56526         }
56527         Roo.SplitLayoutRegion.superclass.show.call(this);
56528     },
56529     
56530     beforeSlide: function(){
56531         if(Roo.isGecko){// firefox overflow auto bug workaround
56532             this.bodyEl.clip();
56533             if(this.tabs) {
56534                 this.tabs.bodyEl.clip();
56535             }
56536             if(this.activePanel){
56537                 this.activePanel.getEl().clip();
56538                 
56539                 if(this.activePanel.beforeSlide){
56540                     this.activePanel.beforeSlide();
56541                 }
56542             }
56543         }
56544     },
56545     
56546     afterSlide : function(){
56547         if(Roo.isGecko){// firefox overflow auto bug workaround
56548             this.bodyEl.unclip();
56549             if(this.tabs) {
56550                 this.tabs.bodyEl.unclip();
56551             }
56552             if(this.activePanel){
56553                 this.activePanel.getEl().unclip();
56554                 if(this.activePanel.afterSlide){
56555                     this.activePanel.afterSlide();
56556                 }
56557             }
56558         }
56559     },
56560
56561     initAutoHide : function(){
56562         if(this.autoHide !== false){
56563             if(!this.autoHideHd){
56564                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56565                 this.autoHideHd = {
56566                     "mouseout": function(e){
56567                         if(!e.within(this.el, true)){
56568                             st.delay(500);
56569                         }
56570                     },
56571                     "mouseover" : function(e){
56572                         st.cancel();
56573                     },
56574                     scope : this
56575                 };
56576             }
56577             this.el.on(this.autoHideHd);
56578         }
56579     },
56580
56581     clearAutoHide : function(){
56582         if(this.autoHide !== false){
56583             this.el.un("mouseout", this.autoHideHd.mouseout);
56584             this.el.un("mouseover", this.autoHideHd.mouseover);
56585         }
56586     },
56587
56588     clearMonitor : function(){
56589         Roo.get(document).un("click", this.slideInIf, this);
56590     },
56591
56592     // these names are backwards but not changed for compat
56593     slideOut : function(){
56594         if(this.isSlid || this.el.hasActiveFx()){
56595             return;
56596         }
56597         this.isSlid = true;
56598         if(this.collapseBtn){
56599             this.collapseBtn.hide();
56600         }
56601         this.closeBtnState = this.closeBtn.getStyle('display');
56602         this.closeBtn.hide();
56603         if(this.stickBtn){
56604             this.stickBtn.show();
56605         }
56606         this.el.show();
56607         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56608         this.beforeSlide();
56609         this.el.setStyle("z-index", 10001);
56610         this.el.slideIn(this.getSlideAnchor(), {
56611             callback: function(){
56612                 this.afterSlide();
56613                 this.initAutoHide();
56614                 Roo.get(document).on("click", this.slideInIf, this);
56615                 this.fireEvent("slideshow", this);
56616             },
56617             scope: this,
56618             block: true
56619         });
56620     },
56621
56622     afterSlideIn : function(){
56623         this.clearAutoHide();
56624         this.isSlid = false;
56625         this.clearMonitor();
56626         this.el.setStyle("z-index", "");
56627         if(this.collapseBtn){
56628             this.collapseBtn.show();
56629         }
56630         this.closeBtn.setStyle('display', this.closeBtnState);
56631         if(this.stickBtn){
56632             this.stickBtn.hide();
56633         }
56634         this.fireEvent("slidehide", this);
56635     },
56636
56637     slideIn : function(cb){
56638         if(!this.isSlid || this.el.hasActiveFx()){
56639             Roo.callback(cb);
56640             return;
56641         }
56642         this.isSlid = false;
56643         this.beforeSlide();
56644         this.el.slideOut(this.getSlideAnchor(), {
56645             callback: function(){
56646                 this.el.setLeftTop(-10000, -10000);
56647                 this.afterSlide();
56648                 this.afterSlideIn();
56649                 Roo.callback(cb);
56650             },
56651             scope: this,
56652             block: true
56653         });
56654     },
56655     
56656     slideInIf : function(e){
56657         if(!e.within(this.el)){
56658             this.slideIn();
56659         }
56660     },
56661
56662     animateCollapse : function(){
56663         this.beforeSlide();
56664         this.el.setStyle("z-index", 20000);
56665         var anchor = this.getSlideAnchor();
56666         this.el.slideOut(anchor, {
56667             callback : function(){
56668                 this.el.setStyle("z-index", "");
56669                 this.collapsedEl.slideIn(anchor, {duration:.3});
56670                 this.afterSlide();
56671                 this.el.setLocation(-10000,-10000);
56672                 this.el.hide();
56673                 this.fireEvent("collapsed", this);
56674             },
56675             scope: this,
56676             block: true
56677         });
56678     },
56679
56680     animateExpand : function(){
56681         this.beforeSlide();
56682         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56683         this.el.setStyle("z-index", 20000);
56684         this.collapsedEl.hide({
56685             duration:.1
56686         });
56687         this.el.slideIn(this.getSlideAnchor(), {
56688             callback : function(){
56689                 this.el.setStyle("z-index", "");
56690                 this.afterSlide();
56691                 if(this.split){
56692                     this.split.el.show();
56693                 }
56694                 this.fireEvent("invalidated", this);
56695                 this.fireEvent("expanded", this);
56696             },
56697             scope: this,
56698             block: true
56699         });
56700     },
56701
56702     anchors : {
56703         "west" : "left",
56704         "east" : "right",
56705         "north" : "top",
56706         "south" : "bottom"
56707     },
56708
56709     sanchors : {
56710         "west" : "l",
56711         "east" : "r",
56712         "north" : "t",
56713         "south" : "b"
56714     },
56715
56716     canchors : {
56717         "west" : "tl-tr",
56718         "east" : "tr-tl",
56719         "north" : "tl-bl",
56720         "south" : "bl-tl"
56721     },
56722
56723     getAnchor : function(){
56724         return this.anchors[this.position];
56725     },
56726
56727     getCollapseAnchor : function(){
56728         return this.canchors[this.position];
56729     },
56730
56731     getSlideAnchor : function(){
56732         return this.sanchors[this.position];
56733     },
56734
56735     getAlignAdj : function(){
56736         var cm = this.cmargins;
56737         switch(this.position){
56738             case "west":
56739                 return [0, 0];
56740             break;
56741             case "east":
56742                 return [0, 0];
56743             break;
56744             case "north":
56745                 return [0, 0];
56746             break;
56747             case "south":
56748                 return [0, 0];
56749             break;
56750         }
56751     },
56752
56753     getExpandAdj : function(){
56754         var c = this.collapsedEl, cm = this.cmargins;
56755         switch(this.position){
56756             case "west":
56757                 return [-(cm.right+c.getWidth()+cm.left), 0];
56758             break;
56759             case "east":
56760                 return [cm.right+c.getWidth()+cm.left, 0];
56761             break;
56762             case "north":
56763                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56764             break;
56765             case "south":
56766                 return [0, cm.top+cm.bottom+c.getHeight()];
56767             break;
56768         }
56769     }
56770 });/*
56771  * Based on:
56772  * Ext JS Library 1.1.1
56773  * Copyright(c) 2006-2007, Ext JS, LLC.
56774  *
56775  * Originally Released Under LGPL - original licence link has changed is not relivant.
56776  *
56777  * Fork - LGPL
56778  * <script type="text/javascript">
56779  */
56780 /*
56781  * These classes are private internal classes
56782  */
56783 Roo.CenterLayoutRegion = function(mgr, config){
56784     Roo.LayoutRegion.call(this, mgr, config, "center");
56785     this.visible = true;
56786     this.minWidth = config.minWidth || 20;
56787     this.minHeight = config.minHeight || 20;
56788 };
56789
56790 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56791     hide : function(){
56792         // center panel can't be hidden
56793     },
56794     
56795     show : function(){
56796         // center panel can't be hidden
56797     },
56798     
56799     getMinWidth: function(){
56800         return this.minWidth;
56801     },
56802     
56803     getMinHeight: function(){
56804         return this.minHeight;
56805     }
56806 });
56807
56808
56809 Roo.NorthLayoutRegion = function(mgr, config){
56810     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56811     if(this.split){
56812         this.split.placement = Roo.SplitBar.TOP;
56813         this.split.orientation = Roo.SplitBar.VERTICAL;
56814         this.split.el.addClass("x-layout-split-v");
56815     }
56816     var size = config.initialSize || config.height;
56817     if(typeof size != "undefined"){
56818         this.el.setHeight(size);
56819     }
56820 };
56821 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56822     orientation: Roo.SplitBar.VERTICAL,
56823     getBox : function(){
56824         if(this.collapsed){
56825             return this.collapsedEl.getBox();
56826         }
56827         var box = this.el.getBox();
56828         if(this.split){
56829             box.height += this.split.el.getHeight();
56830         }
56831         return box;
56832     },
56833     
56834     updateBox : function(box){
56835         if(this.split && !this.collapsed){
56836             box.height -= this.split.el.getHeight();
56837             this.split.el.setLeft(box.x);
56838             this.split.el.setTop(box.y+box.height);
56839             this.split.el.setWidth(box.width);
56840         }
56841         if(this.collapsed){
56842             this.updateBody(box.width, null);
56843         }
56844         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56845     }
56846 });
56847
56848 Roo.SouthLayoutRegion = function(mgr, config){
56849     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56850     if(this.split){
56851         this.split.placement = Roo.SplitBar.BOTTOM;
56852         this.split.orientation = Roo.SplitBar.VERTICAL;
56853         this.split.el.addClass("x-layout-split-v");
56854     }
56855     var size = config.initialSize || config.height;
56856     if(typeof size != "undefined"){
56857         this.el.setHeight(size);
56858     }
56859 };
56860 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56861     orientation: Roo.SplitBar.VERTICAL,
56862     getBox : function(){
56863         if(this.collapsed){
56864             return this.collapsedEl.getBox();
56865         }
56866         var box = this.el.getBox();
56867         if(this.split){
56868             var sh = this.split.el.getHeight();
56869             box.height += sh;
56870             box.y -= sh;
56871         }
56872         return box;
56873     },
56874     
56875     updateBox : function(box){
56876         if(this.split && !this.collapsed){
56877             var sh = this.split.el.getHeight();
56878             box.height -= sh;
56879             box.y += sh;
56880             this.split.el.setLeft(box.x);
56881             this.split.el.setTop(box.y-sh);
56882             this.split.el.setWidth(box.width);
56883         }
56884         if(this.collapsed){
56885             this.updateBody(box.width, null);
56886         }
56887         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56888     }
56889 });
56890
56891 Roo.EastLayoutRegion = function(mgr, config){
56892     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56893     if(this.split){
56894         this.split.placement = Roo.SplitBar.RIGHT;
56895         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56896         this.split.el.addClass("x-layout-split-h");
56897     }
56898     var size = config.initialSize || config.width;
56899     if(typeof size != "undefined"){
56900         this.el.setWidth(size);
56901     }
56902 };
56903 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56904     orientation: Roo.SplitBar.HORIZONTAL,
56905     getBox : function(){
56906         if(this.collapsed){
56907             return this.collapsedEl.getBox();
56908         }
56909         var box = this.el.getBox();
56910         if(this.split){
56911             var sw = this.split.el.getWidth();
56912             box.width += sw;
56913             box.x -= sw;
56914         }
56915         return box;
56916     },
56917
56918     updateBox : function(box){
56919         if(this.split && !this.collapsed){
56920             var sw = this.split.el.getWidth();
56921             box.width -= sw;
56922             this.split.el.setLeft(box.x);
56923             this.split.el.setTop(box.y);
56924             this.split.el.setHeight(box.height);
56925             box.x += sw;
56926         }
56927         if(this.collapsed){
56928             this.updateBody(null, box.height);
56929         }
56930         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56931     }
56932 });
56933
56934 Roo.WestLayoutRegion = function(mgr, config){
56935     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56936     if(this.split){
56937         this.split.placement = Roo.SplitBar.LEFT;
56938         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56939         this.split.el.addClass("x-layout-split-h");
56940     }
56941     var size = config.initialSize || config.width;
56942     if(typeof size != "undefined"){
56943         this.el.setWidth(size);
56944     }
56945 };
56946 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56947     orientation: Roo.SplitBar.HORIZONTAL,
56948     getBox : function(){
56949         if(this.collapsed){
56950             return this.collapsedEl.getBox();
56951         }
56952         var box = this.el.getBox();
56953         if(this.split){
56954             box.width += this.split.el.getWidth();
56955         }
56956         return box;
56957     },
56958     
56959     updateBox : function(box){
56960         if(this.split && !this.collapsed){
56961             var sw = this.split.el.getWidth();
56962             box.width -= sw;
56963             this.split.el.setLeft(box.x+box.width);
56964             this.split.el.setTop(box.y);
56965             this.split.el.setHeight(box.height);
56966         }
56967         if(this.collapsed){
56968             this.updateBody(null, box.height);
56969         }
56970         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56971     }
56972 });
56973 /*
56974  * Based on:
56975  * Ext JS Library 1.1.1
56976  * Copyright(c) 2006-2007, Ext JS, LLC.
56977  *
56978  * Originally Released Under LGPL - original licence link has changed is not relivant.
56979  *
56980  * Fork - LGPL
56981  * <script type="text/javascript">
56982  */
56983  
56984  
56985 /*
56986  * Private internal class for reading and applying state
56987  */
56988 Roo.LayoutStateManager = function(layout){
56989      // default empty state
56990      this.state = {
56991         north: {},
56992         south: {},
56993         east: {},
56994         west: {}       
56995     };
56996 };
56997
56998 Roo.LayoutStateManager.prototype = {
56999     init : function(layout, provider){
57000         this.provider = provider;
57001         var state = provider.get(layout.id+"-layout-state");
57002         if(state){
57003             var wasUpdating = layout.isUpdating();
57004             if(!wasUpdating){
57005                 layout.beginUpdate();
57006             }
57007             for(var key in state){
57008                 if(typeof state[key] != "function"){
57009                     var rstate = state[key];
57010                     var r = layout.getRegion(key);
57011                     if(r && rstate){
57012                         if(rstate.size){
57013                             r.resizeTo(rstate.size);
57014                         }
57015                         if(rstate.collapsed == true){
57016                             r.collapse(true);
57017                         }else{
57018                             r.expand(null, true);
57019                         }
57020                     }
57021                 }
57022             }
57023             if(!wasUpdating){
57024                 layout.endUpdate();
57025             }
57026             this.state = state; 
57027         }
57028         this.layout = layout;
57029         layout.on("regionresized", this.onRegionResized, this);
57030         layout.on("regioncollapsed", this.onRegionCollapsed, this);
57031         layout.on("regionexpanded", this.onRegionExpanded, this);
57032     },
57033     
57034     storeState : function(){
57035         this.provider.set(this.layout.id+"-layout-state", this.state);
57036     },
57037     
57038     onRegionResized : function(region, newSize){
57039         this.state[region.getPosition()].size = newSize;
57040         this.storeState();
57041     },
57042     
57043     onRegionCollapsed : function(region){
57044         this.state[region.getPosition()].collapsed = true;
57045         this.storeState();
57046     },
57047     
57048     onRegionExpanded : function(region){
57049         this.state[region.getPosition()].collapsed = false;
57050         this.storeState();
57051     }
57052 };/*
57053  * Based on:
57054  * Ext JS Library 1.1.1
57055  * Copyright(c) 2006-2007, Ext JS, LLC.
57056  *
57057  * Originally Released Under LGPL - original licence link has changed is not relivant.
57058  *
57059  * Fork - LGPL
57060  * <script type="text/javascript">
57061  */
57062 /**
57063  * @class Roo.ContentPanel
57064  * @extends Roo.util.Observable
57065  * @children Roo.form.Form Roo.JsonView Roo.View
57066  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57067  * A basic ContentPanel element.
57068  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
57069  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
57070  * @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
57071  * @cfg {Boolean}   closable      True if the panel can be closed/removed
57072  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
57073  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
57074  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
57075  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
57076  * @cfg {String} title          The title for this panel
57077  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
57078  * @cfg {String} url            Calls {@link #setUrl} with this value
57079  * @cfg {String} region (center|north|south|east|west) [required] which region to put this panel on (when used with xtype constructors)
57080  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
57081  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
57082  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
57083  * @cfg {String}    style  Extra style to add to the content panel
57084  * @cfg {Roo.menu.Menu} menu  popup menu
57085
57086  * @constructor
57087  * Create a new ContentPanel.
57088  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
57089  * @param {String/Object} config A string to set only the title or a config object
57090  * @param {String} content (optional) Set the HTML content for this panel
57091  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
57092  */
57093 Roo.ContentPanel = function(el, config, content){
57094     
57095      
57096     /*
57097     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
57098         config = el;
57099         el = Roo.id();
57100     }
57101     if (config && config.parentLayout) { 
57102         el = config.parentLayout.el.createChild(); 
57103     }
57104     */
57105     if(el.autoCreate){ // xtype is available if this is called from factory
57106         config = el;
57107         el = Roo.id();
57108     }
57109     this.el = Roo.get(el);
57110     if(!this.el && config && config.autoCreate){
57111         if(typeof config.autoCreate == "object"){
57112             if(!config.autoCreate.id){
57113                 config.autoCreate.id = config.id||el;
57114             }
57115             this.el = Roo.DomHelper.append(document.body,
57116                         config.autoCreate, true);
57117         }else{
57118             this.el = Roo.DomHelper.append(document.body,
57119                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
57120         }
57121     }
57122     
57123     
57124     this.closable = false;
57125     this.loaded = false;
57126     this.active = false;
57127     if(typeof config == "string"){
57128         this.title = config;
57129     }else{
57130         Roo.apply(this, config);
57131     }
57132     
57133     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
57134         this.wrapEl = this.el.wrap();
57135         this.toolbar.container = this.el.insertSibling(false, 'before');
57136         this.toolbar = new Roo.Toolbar(this.toolbar);
57137     }
57138     
57139     // xtype created footer. - not sure if will work as we normally have to render first..
57140     if (this.footer && !this.footer.el && this.footer.xtype) {
57141         if (!this.wrapEl) {
57142             this.wrapEl = this.el.wrap();
57143         }
57144     
57145         this.footer.container = this.wrapEl.createChild();
57146          
57147         this.footer = Roo.factory(this.footer, Roo);
57148         
57149     }
57150     
57151     if(this.resizeEl){
57152         this.resizeEl = Roo.get(this.resizeEl, true);
57153     }else{
57154         this.resizeEl = this.el;
57155     }
57156     // handle view.xtype
57157     
57158  
57159     
57160     
57161     this.addEvents({
57162         /**
57163          * @event activate
57164          * Fires when this panel is activated. 
57165          * @param {Roo.ContentPanel} this
57166          */
57167         "activate" : true,
57168         /**
57169          * @event deactivate
57170          * Fires when this panel is activated. 
57171          * @param {Roo.ContentPanel} this
57172          */
57173         "deactivate" : true,
57174
57175         /**
57176          * @event resize
57177          * Fires when this panel is resized if fitToFrame is true.
57178          * @param {Roo.ContentPanel} this
57179          * @param {Number} width The width after any component adjustments
57180          * @param {Number} height The height after any component adjustments
57181          */
57182         "resize" : true,
57183         
57184          /**
57185          * @event render
57186          * Fires when this tab is created
57187          * @param {Roo.ContentPanel} this
57188          */
57189         "render" : true
57190          
57191         
57192     });
57193     
57194
57195     
57196     
57197     if(this.autoScroll){
57198         this.resizeEl.setStyle("overflow", "auto");
57199     } else {
57200         // fix randome scrolling
57201         this.el.on('scroll', function() {
57202             Roo.log('fix random scolling');
57203             this.scrollTo('top',0); 
57204         });
57205     }
57206     content = content || this.content;
57207     if(content){
57208         this.setContent(content);
57209     }
57210     if(config && config.url){
57211         this.setUrl(this.url, this.params, this.loadOnce);
57212     }
57213     
57214     
57215     
57216     Roo.ContentPanel.superclass.constructor.call(this);
57217     
57218     if (this.view && typeof(this.view.xtype) != 'undefined') {
57219         this.view.el = this.el.appendChild(document.createElement("div"));
57220         this.view = Roo.factory(this.view); 
57221         this.view.render  &&  this.view.render(false, '');  
57222     }
57223     
57224     
57225     this.fireEvent('render', this);
57226 };
57227
57228 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
57229     tabTip:'',
57230     setRegion : function(region){
57231         this.region = region;
57232         if(region){
57233            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
57234         }else{
57235            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
57236         } 
57237     },
57238     
57239     /**
57240      * Returns the toolbar for this Panel if one was configured. 
57241      * @return {Roo.Toolbar} 
57242      */
57243     getToolbar : function(){
57244         return this.toolbar;
57245     },
57246     
57247     setActiveState : function(active){
57248         this.active = active;
57249         if(!active){
57250             this.fireEvent("deactivate", this);
57251         }else{
57252             this.fireEvent("activate", this);
57253         }
57254     },
57255     /**
57256      * Updates this panel's element
57257      * @param {String} content The new content
57258      * @param {Boolean} loadScripts (optional) true to look for and process scripts
57259     */
57260     setContent : function(content, loadScripts){
57261         this.el.update(content, loadScripts);
57262     },
57263
57264     ignoreResize : function(w, h){
57265         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
57266             return true;
57267         }else{
57268             this.lastSize = {width: w, height: h};
57269             return false;
57270         }
57271     },
57272     /**
57273      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
57274      * @return {Roo.UpdateManager} The UpdateManager
57275      */
57276     getUpdateManager : function(){
57277         return this.el.getUpdateManager();
57278     },
57279      /**
57280      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
57281      * @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:
57282 <pre><code>
57283 panel.load({
57284     url: "your-url.php",
57285     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
57286     callback: yourFunction,
57287     scope: yourObject, //(optional scope)
57288     discardUrl: false,
57289     nocache: false,
57290     text: "Loading...",
57291     timeout: 30,
57292     scripts: false
57293 });
57294 </code></pre>
57295      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
57296      * 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.
57297      * @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}
57298      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
57299      * @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.
57300      * @return {Roo.ContentPanel} this
57301      */
57302     load : function(){
57303         var um = this.el.getUpdateManager();
57304         um.update.apply(um, arguments);
57305         return this;
57306     },
57307
57308
57309     /**
57310      * 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.
57311      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
57312      * @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)
57313      * @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)
57314      * @return {Roo.UpdateManager} The UpdateManager
57315      */
57316     setUrl : function(url, params, loadOnce){
57317         if(this.refreshDelegate){
57318             this.removeListener("activate", this.refreshDelegate);
57319         }
57320         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
57321         this.on("activate", this.refreshDelegate);
57322         return this.el.getUpdateManager();
57323     },
57324     
57325     _handleRefresh : function(url, params, loadOnce){
57326         if(!loadOnce || !this.loaded){
57327             var updater = this.el.getUpdateManager();
57328             updater.update(url, params, this._setLoaded.createDelegate(this));
57329         }
57330     },
57331     
57332     _setLoaded : function(){
57333         this.loaded = true;
57334     }, 
57335     
57336     /**
57337      * Returns this panel's id
57338      * @return {String} 
57339      */
57340     getId : function(){
57341         return this.el.id;
57342     },
57343     
57344     /** 
57345      * Returns this panel's element - used by regiosn to add.
57346      * @return {Roo.Element} 
57347      */
57348     getEl : function(){
57349         return this.wrapEl || this.el;
57350     },
57351     
57352     adjustForComponents : function(width, height)
57353     {
57354         //Roo.log('adjustForComponents ');
57355         if(this.resizeEl != this.el){
57356             width -= this.el.getFrameWidth('lr');
57357             height -= this.el.getFrameWidth('tb');
57358         }
57359         if(this.toolbar){
57360             var te = this.toolbar.getEl();
57361             height -= te.getHeight();
57362             te.setWidth(width);
57363         }
57364         if(this.footer){
57365             var te = this.footer.getEl();
57366             //Roo.log("footer:" + te.getHeight());
57367             
57368             height -= te.getHeight();
57369             te.setWidth(width);
57370         }
57371         
57372         
57373         if(this.adjustments){
57374             width += this.adjustments[0];
57375             height += this.adjustments[1];
57376         }
57377         return {"width": width, "height": height};
57378     },
57379     
57380     setSize : function(width, height){
57381         if(this.fitToFrame && !this.ignoreResize(width, height)){
57382             if(this.fitContainer && this.resizeEl != this.el){
57383                 this.el.setSize(width, height);
57384             }
57385             var size = this.adjustForComponents(width, height);
57386             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57387             this.fireEvent('resize', this, size.width, size.height);
57388         }
57389     },
57390     
57391     /**
57392      * Returns this panel's title
57393      * @return {String} 
57394      */
57395     getTitle : function(){
57396         return this.title;
57397     },
57398     
57399     /**
57400      * Set this panel's title
57401      * @param {String} title
57402      */
57403     setTitle : function(title){
57404         this.title = title;
57405         if(this.region){
57406             this.region.updatePanelTitle(this, title);
57407         }
57408     },
57409     
57410     /**
57411      * Returns true is this panel was configured to be closable
57412      * @return {Boolean} 
57413      */
57414     isClosable : function(){
57415         return this.closable;
57416     },
57417     
57418     beforeSlide : function(){
57419         this.el.clip();
57420         this.resizeEl.clip();
57421     },
57422     
57423     afterSlide : function(){
57424         this.el.unclip();
57425         this.resizeEl.unclip();
57426     },
57427     
57428     /**
57429      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57430      *   Will fail silently if the {@link #setUrl} method has not been called.
57431      *   This does not activate the panel, just updates its content.
57432      */
57433     refresh : function(){
57434         if(this.refreshDelegate){
57435            this.loaded = false;
57436            this.refreshDelegate();
57437         }
57438     },
57439     
57440     /**
57441      * Destroys this panel
57442      */
57443     destroy : function(){
57444         this.el.removeAllListeners();
57445         var tempEl = document.createElement("span");
57446         tempEl.appendChild(this.el.dom);
57447         tempEl.innerHTML = "";
57448         this.el.remove();
57449         this.el = null;
57450     },
57451     
57452     /**
57453      * form - if the content panel contains a form - this is a reference to it.
57454      * @type {Roo.form.Form}
57455      */
57456     form : false,
57457     /**
57458      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57459      *    This contains a reference to it.
57460      * @type {Roo.View}
57461      */
57462     view : false,
57463     
57464       /**
57465      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57466      * <pre><code>
57467
57468 layout.addxtype({
57469        xtype : 'Form',
57470        items: [ .... ]
57471    }
57472 );
57473
57474 </code></pre>
57475      * @param {Object} cfg Xtype definition of item to add.
57476      */
57477     
57478     addxtype : function(cfg) {
57479         // add form..
57480         if (cfg.xtype.match(/^Form$/)) {
57481             
57482             var el;
57483             //if (this.footer) {
57484             //    el = this.footer.container.insertSibling(false, 'before');
57485             //} else {
57486                 el = this.el.createChild();
57487             //}
57488
57489             this.form = new  Roo.form.Form(cfg);
57490             
57491             
57492             if ( this.form.allItems.length) {
57493                 this.form.render(el.dom);
57494             }
57495             return this.form;
57496         }
57497         // should only have one of theses..
57498         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57499             // views.. should not be just added - used named prop 'view''
57500             
57501             cfg.el = this.el.appendChild(document.createElement("div"));
57502             // factory?
57503             
57504             var ret = new Roo.factory(cfg);
57505              
57506              ret.render && ret.render(false, ''); // render blank..
57507             this.view = ret;
57508             return ret;
57509         }
57510         return false;
57511     }
57512 });
57513
57514 /**
57515  * @class Roo.GridPanel
57516  * @extends Roo.ContentPanel
57517  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57518  * @constructor
57519  * Create a new GridPanel.
57520  * @cfg {Roo.grid.Grid} grid The grid for this panel
57521  */
57522 Roo.GridPanel = function(grid, config){
57523     
57524     // universal ctor...
57525     if (typeof(grid.grid) != 'undefined') {
57526         config = grid;
57527         grid = config.grid;
57528     }
57529     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57530         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57531         
57532     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57533     
57534     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57535     
57536     if(this.toolbar){
57537         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57538     }
57539     // xtype created footer. - not sure if will work as we normally have to render first..
57540     if (this.footer && !this.footer.el && this.footer.xtype) {
57541         
57542         this.footer.container = this.grid.getView().getFooterPanel(true);
57543         this.footer.dataSource = this.grid.dataSource;
57544         this.footer = Roo.factory(this.footer, Roo);
57545         
57546     }
57547     
57548     grid.monitorWindowResize = false; // turn off autosizing
57549     grid.autoHeight = false;
57550     grid.autoWidth = false;
57551     this.grid = grid;
57552     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57553 };
57554
57555 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57556     getId : function(){
57557         return this.grid.id;
57558     },
57559     
57560     /**
57561      * Returns the grid for this panel
57562      * @return {Roo.grid.Grid} 
57563      */
57564     getGrid : function(){
57565         return this.grid;    
57566     },
57567     
57568     setSize : function(width, height){
57569         if(!this.ignoreResize(width, height)){
57570             var grid = this.grid;
57571             var size = this.adjustForComponents(width, height);
57572             grid.getGridEl().setSize(size.width, size.height);
57573             grid.autoSize();
57574         }
57575     },
57576     
57577     beforeSlide : function(){
57578         this.grid.getView().scroller.clip();
57579     },
57580     
57581     afterSlide : function(){
57582         this.grid.getView().scroller.unclip();
57583     },
57584     
57585     destroy : function(){
57586         this.grid.destroy();
57587         delete this.grid;
57588         Roo.GridPanel.superclass.destroy.call(this); 
57589     }
57590 });
57591
57592
57593 /**
57594  * @class Roo.NestedLayoutPanel
57595  * @extends Roo.ContentPanel
57596  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57597  * @cfg Roo.BorderLayout} layout   [required] The layout for this panel
57598  *
57599  * 
57600  * @constructor
57601  * Create a new NestedLayoutPanel.
57602  * 
57603  * 
57604  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57605  * @param {String/Object} config A string to set only the title or a config object
57606  */
57607 Roo.NestedLayoutPanel = function(layout, config)
57608 {
57609     // construct with only one argument..
57610     /* FIXME - implement nicer consturctors
57611     if (layout.layout) {
57612         config = layout;
57613         layout = config.layout;
57614         delete config.layout;
57615     }
57616     if (layout.xtype && !layout.getEl) {
57617         // then layout needs constructing..
57618         layout = Roo.factory(layout, Roo);
57619     }
57620     */
57621     
57622     
57623     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57624     
57625     layout.monitorWindowResize = false; // turn off autosizing
57626     this.layout = layout;
57627     this.layout.getEl().addClass("x-layout-nested-layout");
57628     
57629     
57630     
57631     
57632 };
57633
57634 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57635
57636     setSize : function(width, height){
57637         if(!this.ignoreResize(width, height)){
57638             var size = this.adjustForComponents(width, height);
57639             var el = this.layout.getEl();
57640             el.setSize(size.width, size.height);
57641             var touch = el.dom.offsetWidth;
57642             this.layout.layout();
57643             // ie requires a double layout on the first pass
57644             if(Roo.isIE && !this.initialized){
57645                 this.initialized = true;
57646                 this.layout.layout();
57647             }
57648         }
57649     },
57650     
57651     // activate all subpanels if not currently active..
57652     
57653     setActiveState : function(active){
57654         this.active = active;
57655         if(!active){
57656             this.fireEvent("deactivate", this);
57657             return;
57658         }
57659         
57660         this.fireEvent("activate", this);
57661         // not sure if this should happen before or after..
57662         if (!this.layout) {
57663             return; // should not happen..
57664         }
57665         var reg = false;
57666         for (var r in this.layout.regions) {
57667             reg = this.layout.getRegion(r);
57668             if (reg.getActivePanel()) {
57669                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57670                 reg.setActivePanel(reg.getActivePanel());
57671                 continue;
57672             }
57673             if (!reg.panels.length) {
57674                 continue;
57675             }
57676             reg.showPanel(reg.getPanel(0));
57677         }
57678         
57679         
57680         
57681         
57682     },
57683     
57684     /**
57685      * Returns the nested BorderLayout for this panel
57686      * @return {Roo.BorderLayout} 
57687      */
57688     getLayout : function(){
57689         return this.layout;
57690     },
57691     
57692      /**
57693      * Adds a xtype elements to the layout of the nested panel
57694      * <pre><code>
57695
57696 panel.addxtype({
57697        xtype : 'ContentPanel',
57698        region: 'west',
57699        items: [ .... ]
57700    }
57701 );
57702
57703 panel.addxtype({
57704         xtype : 'NestedLayoutPanel',
57705         region: 'west',
57706         layout: {
57707            center: { },
57708            west: { }   
57709         },
57710         items : [ ... list of content panels or nested layout panels.. ]
57711    }
57712 );
57713 </code></pre>
57714      * @param {Object} cfg Xtype definition of item to add.
57715      */
57716     addxtype : function(cfg) {
57717         return this.layout.addxtype(cfg);
57718     
57719     }
57720 });
57721
57722 Roo.ScrollPanel = function(el, config, content){
57723     config = config || {};
57724     config.fitToFrame = true;
57725     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57726     
57727     this.el.dom.style.overflow = "hidden";
57728     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57729     this.el.removeClass("x-layout-inactive-content");
57730     this.el.on("mousewheel", this.onWheel, this);
57731
57732     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57733     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57734     up.unselectable(); down.unselectable();
57735     up.on("click", this.scrollUp, this);
57736     down.on("click", this.scrollDown, this);
57737     up.addClassOnOver("x-scroller-btn-over");
57738     down.addClassOnOver("x-scroller-btn-over");
57739     up.addClassOnClick("x-scroller-btn-click");
57740     down.addClassOnClick("x-scroller-btn-click");
57741     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57742
57743     this.resizeEl = this.el;
57744     this.el = wrap; this.up = up; this.down = down;
57745 };
57746
57747 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57748     increment : 100,
57749     wheelIncrement : 5,
57750     scrollUp : function(){
57751         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57752     },
57753
57754     scrollDown : function(){
57755         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57756     },
57757
57758     afterScroll : function(){
57759         var el = this.resizeEl;
57760         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57761         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57762         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57763     },
57764
57765     setSize : function(){
57766         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57767         this.afterScroll();
57768     },
57769
57770     onWheel : function(e){
57771         var d = e.getWheelDelta();
57772         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57773         this.afterScroll();
57774         e.stopEvent();
57775     },
57776
57777     setContent : function(content, loadScripts){
57778         this.resizeEl.update(content, loadScripts);
57779     }
57780
57781 });
57782
57783
57784
57785 /**
57786  * @class Roo.TreePanel
57787  * @extends Roo.ContentPanel
57788  * @parent Roo.BorderLayout Roo.LayoutDialog builder
57789  * Treepanel component
57790  * 
57791  * @constructor
57792  * Create a new TreePanel. - defaults to fit/scoll contents.
57793  * @param {String/Object} config A string to set only the panel's title, or a config object
57794  */
57795 Roo.TreePanel = function(config){
57796     var el = config.el;
57797     var tree = config.tree;
57798     delete config.tree; 
57799     delete config.el; // hopefull!
57800     
57801     // wrapper for IE7 strict & safari scroll issue
57802     
57803     var treeEl = el.createChild();
57804     config.resizeEl = treeEl;
57805     
57806     
57807     
57808     Roo.TreePanel.superclass.constructor.call(this, el, config);
57809  
57810  
57811     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57812     //console.log(tree);
57813     this.on('activate', function()
57814     {
57815         if (this.tree.rendered) {
57816             return;
57817         }
57818         //console.log('render tree');
57819         this.tree.render();
57820     });
57821     // this should not be needed.. - it's actually the 'el' that resizes?
57822     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57823     
57824     //this.on('resize',  function (cp, w, h) {
57825     //        this.tree.innerCt.setWidth(w);
57826     //        this.tree.innerCt.setHeight(h);
57827     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57828     //});
57829
57830         
57831     
57832 };
57833
57834 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57835     fitToFrame : true,
57836     autoScroll : true,
57837     /*
57838      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57839      */
57840     tree : false
57841
57842 });
57843
57844
57845
57846
57847
57848
57849
57850
57851
57852
57853
57854 /*
57855  * Based on:
57856  * Ext JS Library 1.1.1
57857  * Copyright(c) 2006-2007, Ext JS, LLC.
57858  *
57859  * Originally Released Under LGPL - original licence link has changed is not relivant.
57860  *
57861  * Fork - LGPL
57862  * <script type="text/javascript">
57863  */
57864  
57865
57866 /**
57867  * @class Roo.ReaderLayout
57868  * @extends Roo.BorderLayout
57869  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57870  * center region containing two nested regions (a top one for a list view and one for item preview below),
57871  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57872  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57873  * expedites the setup of the overall layout and regions for this common application style.
57874  * Example:
57875  <pre><code>
57876 var reader = new Roo.ReaderLayout();
57877 var CP = Roo.ContentPanel;  // shortcut for adding
57878
57879 reader.beginUpdate();
57880 reader.add("north", new CP("north", "North"));
57881 reader.add("west", new CP("west", {title: "West"}));
57882 reader.add("east", new CP("east", {title: "East"}));
57883
57884 reader.regions.listView.add(new CP("listView", "List"));
57885 reader.regions.preview.add(new CP("preview", "Preview"));
57886 reader.endUpdate();
57887 </code></pre>
57888 * @constructor
57889 * Create a new ReaderLayout
57890 * @param {Object} config Configuration options
57891 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57892 * document.body if omitted)
57893 */
57894 Roo.ReaderLayout = function(config, renderTo){
57895     var c = config || {size:{}};
57896     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57897         north: c.north !== false ? Roo.apply({
57898             split:false,
57899             initialSize: 32,
57900             titlebar: false
57901         }, c.north) : false,
57902         west: c.west !== false ? Roo.apply({
57903             split:true,
57904             initialSize: 200,
57905             minSize: 175,
57906             maxSize: 400,
57907             titlebar: true,
57908             collapsible: true,
57909             animate: true,
57910             margins:{left:5,right:0,bottom:5,top:5},
57911             cmargins:{left:5,right:5,bottom:5,top:5}
57912         }, c.west) : false,
57913         east: c.east !== false ? Roo.apply({
57914             split:true,
57915             initialSize: 200,
57916             minSize: 175,
57917             maxSize: 400,
57918             titlebar: true,
57919             collapsible: true,
57920             animate: true,
57921             margins:{left:0,right:5,bottom:5,top:5},
57922             cmargins:{left:5,right:5,bottom:5,top:5}
57923         }, c.east) : false,
57924         center: Roo.apply({
57925             tabPosition: 'top',
57926             autoScroll:false,
57927             closeOnTab: true,
57928             titlebar:false,
57929             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57930         }, c.center)
57931     });
57932
57933     this.el.addClass('x-reader');
57934
57935     this.beginUpdate();
57936
57937     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57938         south: c.preview !== false ? Roo.apply({
57939             split:true,
57940             initialSize: 200,
57941             minSize: 100,
57942             autoScroll:true,
57943             collapsible:true,
57944             titlebar: true,
57945             cmargins:{top:5,left:0, right:0, bottom:0}
57946         }, c.preview) : false,
57947         center: Roo.apply({
57948             autoScroll:false,
57949             titlebar:false,
57950             minHeight:200
57951         }, c.listView)
57952     });
57953     this.add('center', new Roo.NestedLayoutPanel(inner,
57954             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57955
57956     this.endUpdate();
57957
57958     this.regions.preview = inner.getRegion('south');
57959     this.regions.listView = inner.getRegion('center');
57960 };
57961
57962 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57963  * Based on:
57964  * Ext JS Library 1.1.1
57965  * Copyright(c) 2006-2007, Ext JS, LLC.
57966  *
57967  * Originally Released Under LGPL - original licence link has changed is not relivant.
57968  *
57969  * Fork - LGPL
57970  * <script type="text/javascript">
57971  */
57972  
57973 /**
57974  * @class Roo.grid.Grid
57975  * @extends Roo.util.Observable
57976  * This class represents the primary interface of a component based grid control.
57977  * <br><br>Usage:<pre><code>
57978  var grid = new Roo.grid.Grid("my-container-id", {
57979      ds: myDataStore,
57980      cm: myColModel,
57981      selModel: mySelectionModel,
57982      autoSizeColumns: true,
57983      monitorWindowResize: false,
57984      trackMouseOver: true
57985  });
57986  // set any options
57987  grid.render();
57988  * </code></pre>
57989  * <b>Common Problems:</b><br/>
57990  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57991  * element will correct this<br/>
57992  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57993  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57994  * are unpredictable.<br/>
57995  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57996  * grid to calculate dimensions/offsets.<br/>
57997   * @constructor
57998  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57999  * The container MUST have some type of size defined for the grid to fill. The container will be
58000  * automatically set to position relative if it isn't already.
58001  * @param {Object} config A config object that sets properties on this grid.
58002  */
58003 Roo.grid.Grid = function(container, config){
58004         // initialize the container
58005         this.container = Roo.get(container);
58006         this.container.update("");
58007         this.container.setStyle("overflow", "hidden");
58008     this.container.addClass('x-grid-container');
58009
58010     this.id = this.container.id;
58011
58012     Roo.apply(this, config);
58013     // check and correct shorthanded configs
58014     if(this.ds){
58015         this.dataSource = this.ds;
58016         delete this.ds;
58017     }
58018     if(this.cm){
58019         this.colModel = this.cm;
58020         delete this.cm;
58021     }
58022     if(this.sm){
58023         this.selModel = this.sm;
58024         delete this.sm;
58025     }
58026
58027     if (this.selModel) {
58028         this.selModel = Roo.factory(this.selModel, Roo.grid);
58029         this.sm = this.selModel;
58030         this.sm.xmodule = this.xmodule || false;
58031     }
58032     if (typeof(this.colModel.config) == 'undefined') {
58033         this.colModel = new Roo.grid.ColumnModel(this.colModel);
58034         this.cm = this.colModel;
58035         this.cm.xmodule = this.xmodule || false;
58036     }
58037     if (this.dataSource) {
58038         this.dataSource= Roo.factory(this.dataSource, Roo.data);
58039         this.ds = this.dataSource;
58040         this.ds.xmodule = this.xmodule || false;
58041          
58042     }
58043     
58044     
58045     
58046     if(this.width){
58047         this.container.setWidth(this.width);
58048     }
58049
58050     if(this.height){
58051         this.container.setHeight(this.height);
58052     }
58053     /** @private */
58054         this.addEvents({
58055         // raw events
58056         /**
58057          * @event click
58058          * The raw click event for the entire grid.
58059          * @param {Roo.EventObject} e
58060          */
58061         "click" : true,
58062         /**
58063          * @event dblclick
58064          * The raw dblclick event for the entire grid.
58065          * @param {Roo.EventObject} e
58066          */
58067         "dblclick" : true,
58068         /**
58069          * @event contextmenu
58070          * The raw contextmenu event for the entire grid.
58071          * @param {Roo.EventObject} e
58072          */
58073         "contextmenu" : true,
58074         /**
58075          * @event mousedown
58076          * The raw mousedown event for the entire grid.
58077          * @param {Roo.EventObject} e
58078          */
58079         "mousedown" : true,
58080         /**
58081          * @event mouseup
58082          * The raw mouseup event for the entire grid.
58083          * @param {Roo.EventObject} e
58084          */
58085         "mouseup" : true,
58086         /**
58087          * @event mouseover
58088          * The raw mouseover event for the entire grid.
58089          * @param {Roo.EventObject} e
58090          */
58091         "mouseover" : true,
58092         /**
58093          * @event mouseout
58094          * The raw mouseout event for the entire grid.
58095          * @param {Roo.EventObject} e
58096          */
58097         "mouseout" : true,
58098         /**
58099          * @event keypress
58100          * The raw keypress event for the entire grid.
58101          * @param {Roo.EventObject} e
58102          */
58103         "keypress" : true,
58104         /**
58105          * @event keydown
58106          * The raw keydown event for the entire grid.
58107          * @param {Roo.EventObject} e
58108          */
58109         "keydown" : true,
58110
58111         // custom events
58112
58113         /**
58114          * @event cellclick
58115          * Fires when a cell is clicked
58116          * @param {Grid} this
58117          * @param {Number} rowIndex
58118          * @param {Number} columnIndex
58119          * @param {Roo.EventObject} e
58120          */
58121         "cellclick" : true,
58122         /**
58123          * @event celldblclick
58124          * Fires when a cell is double clicked
58125          * @param {Grid} this
58126          * @param {Number} rowIndex
58127          * @param {Number} columnIndex
58128          * @param {Roo.EventObject} e
58129          */
58130         "celldblclick" : true,
58131         /**
58132          * @event rowclick
58133          * Fires when a row is clicked
58134          * @param {Grid} this
58135          * @param {Number} rowIndex
58136          * @param {Roo.EventObject} e
58137          */
58138         "rowclick" : true,
58139         /**
58140          * @event rowdblclick
58141          * Fires when a row is double clicked
58142          * @param {Grid} this
58143          * @param {Number} rowIndex
58144          * @param {Roo.EventObject} e
58145          */
58146         "rowdblclick" : true,
58147         /**
58148          * @event headerclick
58149          * Fires when a header is clicked
58150          * @param {Grid} this
58151          * @param {Number} columnIndex
58152          * @param {Roo.EventObject} e
58153          */
58154         "headerclick" : true,
58155         /**
58156          * @event headerdblclick
58157          * Fires when a header cell is double clicked
58158          * @param {Grid} this
58159          * @param {Number} columnIndex
58160          * @param {Roo.EventObject} e
58161          */
58162         "headerdblclick" : true,
58163         /**
58164          * @event rowcontextmenu
58165          * Fires when a row is right clicked
58166          * @param {Grid} this
58167          * @param {Number} rowIndex
58168          * @param {Roo.EventObject} e
58169          */
58170         "rowcontextmenu" : true,
58171         /**
58172          * @event cellcontextmenu
58173          * Fires when a cell is right clicked
58174          * @param {Grid} this
58175          * @param {Number} rowIndex
58176          * @param {Number} cellIndex
58177          * @param {Roo.EventObject} e
58178          */
58179          "cellcontextmenu" : true,
58180         /**
58181          * @event headercontextmenu
58182          * Fires when a header is right clicked
58183          * @param {Grid} this
58184          * @param {Number} columnIndex
58185          * @param {Roo.EventObject} e
58186          */
58187         "headercontextmenu" : true,
58188         /**
58189          * @event bodyscroll
58190          * Fires when the body element is scrolled
58191          * @param {Number} scrollLeft
58192          * @param {Number} scrollTop
58193          */
58194         "bodyscroll" : true,
58195         /**
58196          * @event columnresize
58197          * Fires when the user resizes a column
58198          * @param {Number} columnIndex
58199          * @param {Number} newSize
58200          */
58201         "columnresize" : true,
58202         /**
58203          * @event columnmove
58204          * Fires when the user moves a column
58205          * @param {Number} oldIndex
58206          * @param {Number} newIndex
58207          */
58208         "columnmove" : true,
58209         /**
58210          * @event startdrag
58211          * Fires when row(s) start being dragged
58212          * @param {Grid} this
58213          * @param {Roo.GridDD} dd The drag drop object
58214          * @param {event} e The raw browser event
58215          */
58216         "startdrag" : true,
58217         /**
58218          * @event enddrag
58219          * Fires when a drag operation is complete
58220          * @param {Grid} this
58221          * @param {Roo.GridDD} dd The drag drop object
58222          * @param {event} e The raw browser event
58223          */
58224         "enddrag" : true,
58225         /**
58226          * @event dragdrop
58227          * Fires when dragged row(s) are dropped on a valid DD target
58228          * @param {Grid} this
58229          * @param {Roo.GridDD} dd The drag drop object
58230          * @param {String} targetId The target drag drop object
58231          * @param {event} e The raw browser event
58232          */
58233         "dragdrop" : true,
58234         /**
58235          * @event dragover
58236          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
58237          * @param {Grid} this
58238          * @param {Roo.GridDD} dd The drag drop object
58239          * @param {String} targetId The target drag drop object
58240          * @param {event} e The raw browser event
58241          */
58242         "dragover" : true,
58243         /**
58244          * @event dragenter
58245          *  Fires when the dragged row(s) first cross another DD target while being dragged
58246          * @param {Grid} this
58247          * @param {Roo.GridDD} dd The drag drop object
58248          * @param {String} targetId The target drag drop object
58249          * @param {event} e The raw browser event
58250          */
58251         "dragenter" : true,
58252         /**
58253          * @event dragout
58254          * Fires when the dragged row(s) leave another DD target while being dragged
58255          * @param {Grid} this
58256          * @param {Roo.GridDD} dd The drag drop object
58257          * @param {String} targetId The target drag drop object
58258          * @param {event} e The raw browser event
58259          */
58260         "dragout" : true,
58261         /**
58262          * @event rowclass
58263          * Fires when a row is rendered, so you can change add a style to it.
58264          * @param {GridView} gridview   The grid view
58265          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
58266          */
58267         'rowclass' : true,
58268
58269         /**
58270          * @event render
58271          * Fires when the grid is rendered
58272          * @param {Grid} grid
58273          */
58274         'render' : true
58275     });
58276
58277     Roo.grid.Grid.superclass.constructor.call(this);
58278 };
58279 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
58280     
58281     /**
58282          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
58283          */
58284         /**
58285          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
58286          */
58287         /**
58288          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
58289          */
58290         /**
58291          * @cfg {Roo.grid.Store} ds The data store for the grid
58292          */
58293         /**
58294          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
58295          */
58296         /**
58297      * @cfg {String} ddGroup - drag drop group.
58298      */
58299       /**
58300      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
58301      */
58302
58303     /**
58304      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
58305      */
58306     minColumnWidth : 25,
58307
58308     /**
58309      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
58310      * <b>on initial render.</b> It is more efficient to explicitly size the columns
58311      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
58312      */
58313     autoSizeColumns : false,
58314
58315     /**
58316      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
58317      */
58318     autoSizeHeaders : true,
58319
58320     /**
58321      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
58322      */
58323     monitorWindowResize : true,
58324
58325     /**
58326      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
58327      * rows measured to get a columns size. Default is 0 (all rows).
58328      */
58329     maxRowsToMeasure : 0,
58330
58331     /**
58332      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
58333      */
58334     trackMouseOver : true,
58335
58336     /**
58337     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
58338     */
58339       /**
58340     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
58341     */
58342     
58343     /**
58344     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
58345     */
58346     enableDragDrop : false,
58347     
58348     /**
58349     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
58350     */
58351     enableColumnMove : true,
58352     
58353     /**
58354     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
58355     */
58356     enableColumnHide : true,
58357     
58358     /**
58359     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
58360     */
58361     enableRowHeightSync : false,
58362     
58363     /**
58364     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
58365     */
58366     stripeRows : true,
58367     
58368     /**
58369     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
58370     */
58371     autoHeight : false,
58372
58373     /**
58374      * @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.
58375      */
58376     autoExpandColumn : false,
58377
58378     /**
58379     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
58380     * Default is 50.
58381     */
58382     autoExpandMin : 50,
58383
58384     /**
58385     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
58386     */
58387     autoExpandMax : 1000,
58388
58389     /**
58390     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58391     */
58392     view : null,
58393
58394     /**
58395     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58396     */
58397     loadMask : false,
58398     /**
58399     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58400     */
58401     dropTarget: false,
58402      /**
58403     * @cfg {boolean} sortColMenu Sort the column order menu when it shows (usefull for long lists..) default false
58404     */ 
58405     sortColMenu : false,
58406     
58407     // private
58408     rendered : false,
58409
58410     /**
58411     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58412     * of a fixed width. Default is false.
58413     */
58414     /**
58415     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58416     */
58417     
58418     
58419     /**
58420     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58421     * %0 is replaced with the number of selected rows.
58422     */
58423     ddText : "{0} selected row{1}",
58424     
58425     
58426     /**
58427      * Called once after all setup has been completed and the grid is ready to be rendered.
58428      * @return {Roo.grid.Grid} this
58429      */
58430     render : function()
58431     {
58432         var c = this.container;
58433         // try to detect autoHeight/width mode
58434         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58435             this.autoHeight = true;
58436         }
58437         var view = this.getView();
58438         view.init(this);
58439
58440         c.on("click", this.onClick, this);
58441         c.on("dblclick", this.onDblClick, this);
58442         c.on("contextmenu", this.onContextMenu, this);
58443         c.on("keydown", this.onKeyDown, this);
58444         if (Roo.isTouch) {
58445             c.on("touchstart", this.onTouchStart, this);
58446         }
58447
58448         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58449
58450         this.getSelectionModel().init(this);
58451
58452         view.render();
58453
58454         if(this.loadMask){
58455             this.loadMask = new Roo.LoadMask(this.container,
58456                     Roo.apply({store:this.dataSource}, this.loadMask));
58457         }
58458         
58459         
58460         if (this.toolbar && this.toolbar.xtype) {
58461             this.toolbar.container = this.getView().getHeaderPanel(true);
58462             this.toolbar = new Roo.Toolbar(this.toolbar);
58463         }
58464         if (this.footer && this.footer.xtype) {
58465             this.footer.dataSource = this.getDataSource();
58466             this.footer.container = this.getView().getFooterPanel(true);
58467             this.footer = Roo.factory(this.footer, Roo);
58468         }
58469         if (this.dropTarget && this.dropTarget.xtype) {
58470             delete this.dropTarget.xtype;
58471             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58472         }
58473         
58474         
58475         this.rendered = true;
58476         this.fireEvent('render', this);
58477         return this;
58478     },
58479
58480     /**
58481      * Reconfigures the grid to use a different Store and Column Model.
58482      * The View will be bound to the new objects and refreshed.
58483      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58484      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58485      */
58486     reconfigure : function(dataSource, colModel){
58487         if(this.loadMask){
58488             this.loadMask.destroy();
58489             this.loadMask = new Roo.LoadMask(this.container,
58490                     Roo.apply({store:dataSource}, this.loadMask));
58491         }
58492         this.view.bind(dataSource, colModel);
58493         this.dataSource = dataSource;
58494         this.colModel = colModel;
58495         this.view.refresh(true);
58496     },
58497     /**
58498      * addColumns
58499      * Add's a column, default at the end..
58500      
58501      * @param {int} position to add (default end)
58502      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58503      */
58504     addColumns : function(pos, ar)
58505     {
58506         
58507         for (var i =0;i< ar.length;i++) {
58508             var cfg = ar[i];
58509             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58510             this.cm.lookup[cfg.id] = cfg;
58511         }
58512         
58513         
58514         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58515             pos = this.cm.config.length; //this.cm.config.push(cfg);
58516         } 
58517         pos = Math.max(0,pos);
58518         ar.unshift(0);
58519         ar.unshift(pos);
58520         this.cm.config.splice.apply(this.cm.config, ar);
58521         
58522         
58523         
58524         this.view.generateRules(this.cm);
58525         this.view.refresh(true);
58526         
58527     },
58528     
58529     
58530     
58531     
58532     // private
58533     onKeyDown : function(e){
58534         this.fireEvent("keydown", e);
58535     },
58536
58537     /**
58538      * Destroy this grid.
58539      * @param {Boolean} removeEl True to remove the element
58540      */
58541     destroy : function(removeEl, keepListeners){
58542         if(this.loadMask){
58543             this.loadMask.destroy();
58544         }
58545         var c = this.container;
58546         c.removeAllListeners();
58547         this.view.destroy();
58548         this.colModel.purgeListeners();
58549         if(!keepListeners){
58550             this.purgeListeners();
58551         }
58552         c.update("");
58553         if(removeEl === true){
58554             c.remove();
58555         }
58556     },
58557
58558     // private
58559     processEvent : function(name, e){
58560         // does this fire select???
58561         //Roo.log('grid:processEvent '  + name);
58562         
58563         if (name != 'touchstart' ) {
58564             this.fireEvent(name, e);    
58565         }
58566         
58567         var t = e.getTarget();
58568         var v = this.view;
58569         var header = v.findHeaderIndex(t);
58570         if(header !== false){
58571             var ename = name == 'touchstart' ? 'click' : name;
58572              
58573             this.fireEvent("header" + ename, this, header, e);
58574         }else{
58575             var row = v.findRowIndex(t);
58576             var cell = v.findCellIndex(t);
58577             if (name == 'touchstart') {
58578                 // first touch is always a click.
58579                 // hopefull this happens after selection is updated.?
58580                 name = false;
58581                 
58582                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58583                     var cs = this.selModel.getSelectedCell();
58584                     if (row == cs[0] && cell == cs[1]){
58585                         name = 'dblclick';
58586                     }
58587                 }
58588                 if (typeof(this.selModel.getSelections) != 'undefined') {
58589                     var cs = this.selModel.getSelections();
58590                     var ds = this.dataSource;
58591                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58592                         name = 'dblclick';
58593                     }
58594                 }
58595                 if (!name) {
58596                     return;
58597                 }
58598             }
58599             
58600             
58601             if(row !== false){
58602                 this.fireEvent("row" + name, this, row, e);
58603                 if(cell !== false){
58604                     this.fireEvent("cell" + name, this, row, cell, e);
58605                 }
58606             }
58607         }
58608     },
58609
58610     // private
58611     onClick : function(e){
58612         this.processEvent("click", e);
58613     },
58614    // private
58615     onTouchStart : function(e){
58616         this.processEvent("touchstart", e);
58617     },
58618
58619     // private
58620     onContextMenu : function(e, t){
58621         this.processEvent("contextmenu", e);
58622     },
58623
58624     // private
58625     onDblClick : function(e){
58626         this.processEvent("dblclick", e);
58627     },
58628
58629     // private
58630     walkCells : function(row, col, step, fn, scope){
58631         var cm = this.colModel, clen = cm.getColumnCount();
58632         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58633         if(step < 0){
58634             if(col < 0){
58635                 row--;
58636                 first = false;
58637             }
58638             while(row >= 0){
58639                 if(!first){
58640                     col = clen-1;
58641                 }
58642                 first = false;
58643                 while(col >= 0){
58644                     if(fn.call(scope || this, row, col, cm) === true){
58645                         return [row, col];
58646                     }
58647                     col--;
58648                 }
58649                 row--;
58650             }
58651         } else {
58652             if(col >= clen){
58653                 row++;
58654                 first = false;
58655             }
58656             while(row < rlen){
58657                 if(!first){
58658                     col = 0;
58659                 }
58660                 first = false;
58661                 while(col < clen){
58662                     if(fn.call(scope || this, row, col, cm) === true){
58663                         return [row, col];
58664                     }
58665                     col++;
58666                 }
58667                 row++;
58668             }
58669         }
58670         return null;
58671     },
58672
58673     // private
58674     getSelections : function(){
58675         return this.selModel.getSelections();
58676     },
58677
58678     /**
58679      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58680      * but if manual update is required this method will initiate it.
58681      */
58682     autoSize : function(){
58683         if(this.rendered){
58684             this.view.layout();
58685             if(this.view.adjustForScroll){
58686                 this.view.adjustForScroll();
58687             }
58688         }
58689     },
58690
58691     /**
58692      * Returns the grid's underlying element.
58693      * @return {Element} The element
58694      */
58695     getGridEl : function(){
58696         return this.container;
58697     },
58698
58699     // private for compatibility, overridden by editor grid
58700     stopEditing : function(){},
58701
58702     /**
58703      * Returns the grid's SelectionModel.
58704      * @return {SelectionModel}
58705      */
58706     getSelectionModel : function(){
58707         if(!this.selModel){
58708             this.selModel = new Roo.grid.RowSelectionModel();
58709         }
58710         return this.selModel;
58711     },
58712
58713     /**
58714      * Returns the grid's DataSource.
58715      * @return {DataSource}
58716      */
58717     getDataSource : function(){
58718         return this.dataSource;
58719     },
58720
58721     /**
58722      * Returns the grid's ColumnModel.
58723      * @return {ColumnModel}
58724      */
58725     getColumnModel : function(){
58726         return this.colModel;
58727     },
58728
58729     /**
58730      * Returns the grid's GridView object.
58731      * @return {GridView}
58732      */
58733     getView : function(){
58734         if(!this.view){
58735             this.view = new Roo.grid.GridView(this.viewConfig);
58736             this.relayEvents(this.view, [
58737                 "beforerowremoved", "beforerowsinserted",
58738                 "beforerefresh", "rowremoved",
58739                 "rowsinserted", "rowupdated" ,"refresh"
58740             ]);
58741         }
58742         return this.view;
58743     },
58744     /**
58745      * Called to get grid's drag proxy text, by default returns this.ddText.
58746      * Override this to put something different in the dragged text.
58747      * @return {String}
58748      */
58749     getDragDropText : function(){
58750         var count = this.selModel.getCount();
58751         return String.format(this.ddText, count, count == 1 ? '' : 's');
58752     }
58753 });
58754 /*
58755  * Based on:
58756  * Ext JS Library 1.1.1
58757  * Copyright(c) 2006-2007, Ext JS, LLC.
58758  *
58759  * Originally Released Under LGPL - original licence link has changed is not relivant.
58760  *
58761  * Fork - LGPL
58762  * <script type="text/javascript">
58763  */
58764  /**
58765  * @class Roo.grid.AbstractGridView
58766  * @extends Roo.util.Observable
58767  * @abstract
58768  * Abstract base class for grid Views
58769  * @constructor
58770  */
58771 Roo.grid.AbstractGridView = function(){
58772         this.grid = null;
58773         
58774         this.events = {
58775             "beforerowremoved" : true,
58776             "beforerowsinserted" : true,
58777             "beforerefresh" : true,
58778             "rowremoved" : true,
58779             "rowsinserted" : true,
58780             "rowupdated" : true,
58781             "refresh" : true
58782         };
58783     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58784 };
58785
58786 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58787     rowClass : "x-grid-row",
58788     cellClass : "x-grid-cell",
58789     tdClass : "x-grid-td",
58790     hdClass : "x-grid-hd",
58791     splitClass : "x-grid-hd-split",
58792     
58793     init: function(grid){
58794         this.grid = grid;
58795                 var cid = this.grid.getGridEl().id;
58796         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58797         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58798         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58799         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58800         },
58801         
58802     getColumnRenderers : function(){
58803         var renderers = [];
58804         var cm = this.grid.colModel;
58805         var colCount = cm.getColumnCount();
58806         for(var i = 0; i < colCount; i++){
58807             renderers[i] = cm.getRenderer(i);
58808         }
58809         return renderers;
58810     },
58811     
58812     getColumnIds : function(){
58813         var ids = [];
58814         var cm = this.grid.colModel;
58815         var colCount = cm.getColumnCount();
58816         for(var i = 0; i < colCount; i++){
58817             ids[i] = cm.getColumnId(i);
58818         }
58819         return ids;
58820     },
58821     
58822     getDataIndexes : function(){
58823         if(!this.indexMap){
58824             this.indexMap = this.buildIndexMap();
58825         }
58826         return this.indexMap.colToData;
58827     },
58828     
58829     getColumnIndexByDataIndex : function(dataIndex){
58830         if(!this.indexMap){
58831             this.indexMap = this.buildIndexMap();
58832         }
58833         return this.indexMap.dataToCol[dataIndex];
58834     },
58835     
58836     /**
58837      * Set a css style for a column dynamically. 
58838      * @param {Number} colIndex The index of the column
58839      * @param {String} name The css property name
58840      * @param {String} value The css value
58841      */
58842     setCSSStyle : function(colIndex, name, value){
58843         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58844         Roo.util.CSS.updateRule(selector, name, value);
58845     },
58846     
58847     generateRules : function(cm){
58848         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58849         Roo.util.CSS.removeStyleSheet(rulesId);
58850         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58851             var cid = cm.getColumnId(i);
58852             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58853                          this.tdSelector, cid, " {\n}\n",
58854                          this.hdSelector, cid, " {\n}\n",
58855                          this.splitSelector, cid, " {\n}\n");
58856         }
58857         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58858     }
58859 });/*
58860  * Based on:
58861  * Ext JS Library 1.1.1
58862  * Copyright(c) 2006-2007, Ext JS, LLC.
58863  *
58864  * Originally Released Under LGPL - original licence link has changed is not relivant.
58865  *
58866  * Fork - LGPL
58867  * <script type="text/javascript">
58868  */
58869
58870 // private
58871 // This is a support class used internally by the Grid components
58872 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58873     this.grid = grid;
58874     this.view = grid.getView();
58875     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58876     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58877     if(hd2){
58878         this.setHandleElId(Roo.id(hd));
58879         this.setOuterHandleElId(Roo.id(hd2));
58880     }
58881     this.scroll = false;
58882 };
58883 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58884     maxDragWidth: 120,
58885     getDragData : function(e){
58886         var t = Roo.lib.Event.getTarget(e);
58887         var h = this.view.findHeaderCell(t);
58888         if(h){
58889             return {ddel: h.firstChild, header:h};
58890         }
58891         return false;
58892     },
58893
58894     onInitDrag : function(e){
58895         this.view.headersDisabled = true;
58896         var clone = this.dragData.ddel.cloneNode(true);
58897         clone.id = Roo.id();
58898         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58899         this.proxy.update(clone);
58900         return true;
58901     },
58902
58903     afterValidDrop : function(){
58904         var v = this.view;
58905         setTimeout(function(){
58906             v.headersDisabled = false;
58907         }, 50);
58908     },
58909
58910     afterInvalidDrop : function(){
58911         var v = this.view;
58912         setTimeout(function(){
58913             v.headersDisabled = false;
58914         }, 50);
58915     }
58916 });
58917 /*
58918  * Based on:
58919  * Ext JS Library 1.1.1
58920  * Copyright(c) 2006-2007, Ext JS, LLC.
58921  *
58922  * Originally Released Under LGPL - original licence link has changed is not relivant.
58923  *
58924  * Fork - LGPL
58925  * <script type="text/javascript">
58926  */
58927 // private
58928 // This is a support class used internally by the Grid components
58929 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58930     this.grid = grid;
58931     this.view = grid.getView();
58932     // split the proxies so they don't interfere with mouse events
58933     this.proxyTop = Roo.DomHelper.append(document.body, {
58934         cls:"col-move-top", html:"&#160;"
58935     }, true);
58936     this.proxyBottom = Roo.DomHelper.append(document.body, {
58937         cls:"col-move-bottom", html:"&#160;"
58938     }, true);
58939     this.proxyTop.hide = this.proxyBottom.hide = function(){
58940         this.setLeftTop(-100,-100);
58941         this.setStyle("visibility", "hidden");
58942     };
58943     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58944     // temporarily disabled
58945     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58946     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58947 };
58948 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58949     proxyOffsets : [-4, -9],
58950     fly: Roo.Element.fly,
58951
58952     getTargetFromEvent : function(e){
58953         var t = Roo.lib.Event.getTarget(e);
58954         var cindex = this.view.findCellIndex(t);
58955         if(cindex !== false){
58956             return this.view.getHeaderCell(cindex);
58957         }
58958         return null;
58959     },
58960
58961     nextVisible : function(h){
58962         var v = this.view, cm = this.grid.colModel;
58963         h = h.nextSibling;
58964         while(h){
58965             if(!cm.isHidden(v.getCellIndex(h))){
58966                 return h;
58967             }
58968             h = h.nextSibling;
58969         }
58970         return null;
58971     },
58972
58973     prevVisible : function(h){
58974         var v = this.view, cm = this.grid.colModel;
58975         h = h.prevSibling;
58976         while(h){
58977             if(!cm.isHidden(v.getCellIndex(h))){
58978                 return h;
58979             }
58980             h = h.prevSibling;
58981         }
58982         return null;
58983     },
58984
58985     positionIndicator : function(h, n, e){
58986         var x = Roo.lib.Event.getPageX(e);
58987         var r = Roo.lib.Dom.getRegion(n.firstChild);
58988         var px, pt, py = r.top + this.proxyOffsets[1];
58989         if((r.right - x) <= (r.right-r.left)/2){
58990             px = r.right+this.view.borderWidth;
58991             pt = "after";
58992         }else{
58993             px = r.left;
58994             pt = "before";
58995         }
58996         var oldIndex = this.view.getCellIndex(h);
58997         var newIndex = this.view.getCellIndex(n);
58998
58999         if(this.grid.colModel.isFixed(newIndex)){
59000             return false;
59001         }
59002
59003         var locked = this.grid.colModel.isLocked(newIndex);
59004
59005         if(pt == "after"){
59006             newIndex++;
59007         }
59008         if(oldIndex < newIndex){
59009             newIndex--;
59010         }
59011         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
59012             return false;
59013         }
59014         px +=  this.proxyOffsets[0];
59015         this.proxyTop.setLeftTop(px, py);
59016         this.proxyTop.show();
59017         if(!this.bottomOffset){
59018             this.bottomOffset = this.view.mainHd.getHeight();
59019         }
59020         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
59021         this.proxyBottom.show();
59022         return pt;
59023     },
59024
59025     onNodeEnter : function(n, dd, e, data){
59026         if(data.header != n){
59027             this.positionIndicator(data.header, n, e);
59028         }
59029     },
59030
59031     onNodeOver : function(n, dd, e, data){
59032         var result = false;
59033         if(data.header != n){
59034             result = this.positionIndicator(data.header, n, e);
59035         }
59036         if(!result){
59037             this.proxyTop.hide();
59038             this.proxyBottom.hide();
59039         }
59040         return result ? this.dropAllowed : this.dropNotAllowed;
59041     },
59042
59043     onNodeOut : function(n, dd, e, data){
59044         this.proxyTop.hide();
59045         this.proxyBottom.hide();
59046     },
59047
59048     onNodeDrop : function(n, dd, e, data){
59049         var h = data.header;
59050         if(h != n){
59051             var cm = this.grid.colModel;
59052             var x = Roo.lib.Event.getPageX(e);
59053             var r = Roo.lib.Dom.getRegion(n.firstChild);
59054             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
59055             var oldIndex = this.view.getCellIndex(h);
59056             var newIndex = this.view.getCellIndex(n);
59057             var locked = cm.isLocked(newIndex);
59058             if(pt == "after"){
59059                 newIndex++;
59060             }
59061             if(oldIndex < newIndex){
59062                 newIndex--;
59063             }
59064             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
59065                 return false;
59066             }
59067             cm.setLocked(oldIndex, locked, true);
59068             cm.moveColumn(oldIndex, newIndex);
59069             this.grid.fireEvent("columnmove", oldIndex, newIndex);
59070             return true;
59071         }
59072         return false;
59073     }
59074 });
59075 /*
59076  * Based on:
59077  * Ext JS Library 1.1.1
59078  * Copyright(c) 2006-2007, Ext JS, LLC.
59079  *
59080  * Originally Released Under LGPL - original licence link has changed is not relivant.
59081  *
59082  * Fork - LGPL
59083  * <script type="text/javascript">
59084  */
59085   
59086 /**
59087  * @class Roo.grid.GridView
59088  * @extends Roo.util.Observable
59089  *
59090  * @constructor
59091  * @param {Object} config
59092  */
59093 Roo.grid.GridView = function(config){
59094     Roo.grid.GridView.superclass.constructor.call(this);
59095     this.el = null;
59096
59097     Roo.apply(this, config);
59098 };
59099
59100 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
59101
59102     unselectable :  'unselectable="on"',
59103     unselectableCls :  'x-unselectable',
59104     
59105     
59106     rowClass : "x-grid-row",
59107
59108     cellClass : "x-grid-col",
59109
59110     tdClass : "x-grid-td",
59111
59112     hdClass : "x-grid-hd",
59113
59114     splitClass : "x-grid-split",
59115
59116     sortClasses : ["sort-asc", "sort-desc"],
59117
59118     enableMoveAnim : false,
59119
59120     hlColor: "C3DAF9",
59121
59122     dh : Roo.DomHelper,
59123
59124     fly : Roo.Element.fly,
59125
59126     css : Roo.util.CSS,
59127
59128     borderWidth: 1,
59129
59130     splitOffset: 3,
59131
59132     scrollIncrement : 22,
59133
59134     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
59135
59136     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
59137
59138     bind : function(ds, cm){
59139         if(this.ds){
59140             this.ds.un("load", this.onLoad, this);
59141             this.ds.un("datachanged", this.onDataChange, this);
59142             this.ds.un("add", this.onAdd, this);
59143             this.ds.un("remove", this.onRemove, this);
59144             this.ds.un("update", this.onUpdate, this);
59145             this.ds.un("clear", this.onClear, this);
59146         }
59147         if(ds){
59148             ds.on("load", this.onLoad, this);
59149             ds.on("datachanged", this.onDataChange, this);
59150             ds.on("add", this.onAdd, this);
59151             ds.on("remove", this.onRemove, this);
59152             ds.on("update", this.onUpdate, this);
59153             ds.on("clear", this.onClear, this);
59154         }
59155         this.ds = ds;
59156
59157         if(this.cm){
59158             this.cm.un("widthchange", this.onColWidthChange, this);
59159             this.cm.un("headerchange", this.onHeaderChange, this);
59160             this.cm.un("hiddenchange", this.onHiddenChange, this);
59161             this.cm.un("columnmoved", this.onColumnMove, this);
59162             this.cm.un("columnlockchange", this.onColumnLock, this);
59163         }
59164         if(cm){
59165             this.generateRules(cm);
59166             cm.on("widthchange", this.onColWidthChange, this);
59167             cm.on("headerchange", this.onHeaderChange, this);
59168             cm.on("hiddenchange", this.onHiddenChange, this);
59169             cm.on("columnmoved", this.onColumnMove, this);
59170             cm.on("columnlockchange", this.onColumnLock, this);
59171         }
59172         this.cm = cm;
59173     },
59174
59175     init: function(grid){
59176         Roo.grid.GridView.superclass.init.call(this, grid);
59177
59178         this.bind(grid.dataSource, grid.colModel);
59179
59180         grid.on("headerclick", this.handleHeaderClick, this);
59181
59182         if(grid.trackMouseOver){
59183             grid.on("mouseover", this.onRowOver, this);
59184             grid.on("mouseout", this.onRowOut, this);
59185         }
59186         grid.cancelTextSelection = function(){};
59187         this.gridId = grid.id;
59188
59189         var tpls = this.templates || {};
59190
59191         if(!tpls.master){
59192             tpls.master = new Roo.Template(
59193                '<div class="x-grid" hidefocus="true">',
59194                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
59195                   '<div class="x-grid-topbar"></div>',
59196                   '<div class="x-grid-scroller"><div></div></div>',
59197                   '<div class="x-grid-locked">',
59198                       '<div class="x-grid-header">{lockedHeader}</div>',
59199                       '<div class="x-grid-body">{lockedBody}</div>',
59200                   "</div>",
59201                   '<div class="x-grid-viewport">',
59202                       '<div class="x-grid-header">{header}</div>',
59203                       '<div class="x-grid-body">{body}</div>',
59204                   "</div>",
59205                   '<div class="x-grid-bottombar"></div>',
59206                  
59207                   '<div class="x-grid-resize-proxy">&#160;</div>',
59208                "</div>"
59209             );
59210             tpls.master.disableformats = true;
59211         }
59212
59213         if(!tpls.header){
59214             tpls.header = new Roo.Template(
59215                '<table border="0" cellspacing="0" cellpadding="0">',
59216                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
59217                "</table>{splits}"
59218             );
59219             tpls.header.disableformats = true;
59220         }
59221         tpls.header.compile();
59222
59223         if(!tpls.hcell){
59224             tpls.hcell = new Roo.Template(
59225                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
59226                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
59227                 "</div></td>"
59228              );
59229              tpls.hcell.disableFormats = true;
59230         }
59231         tpls.hcell.compile();
59232
59233         if(!tpls.hsplit){
59234             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
59235                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
59236             tpls.hsplit.disableFormats = true;
59237         }
59238         tpls.hsplit.compile();
59239
59240         if(!tpls.body){
59241             tpls.body = new Roo.Template(
59242                '<table border="0" cellspacing="0" cellpadding="0">',
59243                "<tbody>{rows}</tbody>",
59244                "</table>"
59245             );
59246             tpls.body.disableFormats = true;
59247         }
59248         tpls.body.compile();
59249
59250         if(!tpls.row){
59251             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
59252             tpls.row.disableFormats = true;
59253         }
59254         tpls.row.compile();
59255
59256         if(!tpls.cell){
59257             tpls.cell = new Roo.Template(
59258                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
59259                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
59260                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
59261                 "</td>"
59262             );
59263             tpls.cell.disableFormats = true;
59264         }
59265         tpls.cell.compile();
59266
59267         this.templates = tpls;
59268     },
59269
59270     // remap these for backwards compat
59271     onColWidthChange : function(){
59272         this.updateColumns.apply(this, arguments);
59273     },
59274     onHeaderChange : function(){
59275         this.updateHeaders.apply(this, arguments);
59276     }, 
59277     onHiddenChange : function(){
59278         this.handleHiddenChange.apply(this, arguments);
59279     },
59280     onColumnMove : function(){
59281         this.handleColumnMove.apply(this, arguments);
59282     },
59283     onColumnLock : function(){
59284         this.handleLockChange.apply(this, arguments);
59285     },
59286
59287     onDataChange : function(){
59288         this.refresh();
59289         this.updateHeaderSortState();
59290     },
59291
59292     onClear : function(){
59293         this.refresh();
59294     },
59295
59296     onUpdate : function(ds, record){
59297         this.refreshRow(record);
59298     },
59299
59300     refreshRow : function(record){
59301         var ds = this.ds, index;
59302         if(typeof record == 'number'){
59303             index = record;
59304             record = ds.getAt(index);
59305         }else{
59306             index = ds.indexOf(record);
59307         }
59308         this.insertRows(ds, index, index, true);
59309         this.onRemove(ds, record, index+1, true);
59310         this.syncRowHeights(index, index);
59311         this.layout();
59312         this.fireEvent("rowupdated", this, index, record);
59313     },
59314
59315     onAdd : function(ds, records, index){
59316         this.insertRows(ds, index, index + (records.length-1));
59317     },
59318
59319     onRemove : function(ds, record, index, isUpdate){
59320         if(isUpdate !== true){
59321             this.fireEvent("beforerowremoved", this, index, record);
59322         }
59323         var bt = this.getBodyTable(), lt = this.getLockedTable();
59324         if(bt.rows[index]){
59325             bt.firstChild.removeChild(bt.rows[index]);
59326         }
59327         if(lt.rows[index]){
59328             lt.firstChild.removeChild(lt.rows[index]);
59329         }
59330         if(isUpdate !== true){
59331             this.stripeRows(index);
59332             this.syncRowHeights(index, index);
59333             this.layout();
59334             this.fireEvent("rowremoved", this, index, record);
59335         }
59336     },
59337
59338     onLoad : function(){
59339         this.scrollToTop();
59340     },
59341
59342     /**
59343      * Scrolls the grid to the top
59344      */
59345     scrollToTop : function(){
59346         if(this.scroller){
59347             this.scroller.dom.scrollTop = 0;
59348             this.syncScroll();
59349         }
59350     },
59351
59352     /**
59353      * Gets a panel in the header of the grid that can be used for toolbars etc.
59354      * After modifying the contents of this panel a call to grid.autoSize() may be
59355      * required to register any changes in size.
59356      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
59357      * @return Roo.Element
59358      */
59359     getHeaderPanel : function(doShow){
59360         if(doShow){
59361             this.headerPanel.show();
59362         }
59363         return this.headerPanel;
59364     },
59365
59366     /**
59367      * Gets a panel in the footer of the grid that can be used for toolbars etc.
59368      * After modifying the contents of this panel a call to grid.autoSize() may be
59369      * required to register any changes in size.
59370      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
59371      * @return Roo.Element
59372      */
59373     getFooterPanel : function(doShow){
59374         if(doShow){
59375             this.footerPanel.show();
59376         }
59377         return this.footerPanel;
59378     },
59379
59380     initElements : function(){
59381         var E = Roo.Element;
59382         var el = this.grid.getGridEl().dom.firstChild;
59383         var cs = el.childNodes;
59384
59385         this.el = new E(el);
59386         
59387          this.focusEl = new E(el.firstChild);
59388         this.focusEl.swallowEvent("click", true);
59389         
59390         this.headerPanel = new E(cs[1]);
59391         this.headerPanel.enableDisplayMode("block");
59392
59393         this.scroller = new E(cs[2]);
59394         this.scrollSizer = new E(this.scroller.dom.firstChild);
59395
59396         this.lockedWrap = new E(cs[3]);
59397         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59398         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59399
59400         this.mainWrap = new E(cs[4]);
59401         this.mainHd = new E(this.mainWrap.dom.firstChild);
59402         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59403
59404         this.footerPanel = new E(cs[5]);
59405         this.footerPanel.enableDisplayMode("block");
59406
59407         this.resizeProxy = new E(cs[6]);
59408
59409         this.headerSelector = String.format(
59410            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59411            this.lockedHd.id, this.mainHd.id
59412         );
59413
59414         this.splitterSelector = String.format(
59415            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59416            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59417         );
59418     },
59419     idToCssName : function(s)
59420     {
59421         return s.replace(/[^a-z0-9]+/ig, '-');
59422     },
59423
59424     getHeaderCell : function(index){
59425         return Roo.DomQuery.select(this.headerSelector)[index];
59426     },
59427
59428     getHeaderCellMeasure : function(index){
59429         return this.getHeaderCell(index).firstChild;
59430     },
59431
59432     getHeaderCellText : function(index){
59433         return this.getHeaderCell(index).firstChild.firstChild;
59434     },
59435
59436     getLockedTable : function(){
59437         return this.lockedBody.dom.firstChild;
59438     },
59439
59440     getBodyTable : function(){
59441         return this.mainBody.dom.firstChild;
59442     },
59443
59444     getLockedRow : function(index){
59445         return this.getLockedTable().rows[index];
59446     },
59447
59448     getRow : function(index){
59449         return this.getBodyTable().rows[index];
59450     },
59451
59452     getRowComposite : function(index){
59453         if(!this.rowEl){
59454             this.rowEl = new Roo.CompositeElementLite();
59455         }
59456         var els = [], lrow, mrow;
59457         if(lrow = this.getLockedRow(index)){
59458             els.push(lrow);
59459         }
59460         if(mrow = this.getRow(index)){
59461             els.push(mrow);
59462         }
59463         this.rowEl.elements = els;
59464         return this.rowEl;
59465     },
59466     /**
59467      * Gets the 'td' of the cell
59468      * 
59469      * @param {Integer} rowIndex row to select
59470      * @param {Integer} colIndex column to select
59471      * 
59472      * @return {Object} 
59473      */
59474     getCell : function(rowIndex, colIndex){
59475         var locked = this.cm.getLockedCount();
59476         var source;
59477         if(colIndex < locked){
59478             source = this.lockedBody.dom.firstChild;
59479         }else{
59480             source = this.mainBody.dom.firstChild;
59481             colIndex -= locked;
59482         }
59483         return source.rows[rowIndex].childNodes[colIndex];
59484     },
59485
59486     getCellText : function(rowIndex, colIndex){
59487         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59488     },
59489
59490     getCellBox : function(cell){
59491         var b = this.fly(cell).getBox();
59492         if(Roo.isOpera){ // opera fails to report the Y
59493             b.y = cell.offsetTop + this.mainBody.getY();
59494         }
59495         return b;
59496     },
59497
59498     getCellIndex : function(cell){
59499         var id = String(cell.className).match(this.cellRE);
59500         if(id){
59501             return parseInt(id[1], 10);
59502         }
59503         return 0;
59504     },
59505
59506     findHeaderIndex : function(n){
59507         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59508         return r ? this.getCellIndex(r) : false;
59509     },
59510
59511     findHeaderCell : function(n){
59512         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59513         return r ? r : false;
59514     },
59515
59516     findRowIndex : function(n){
59517         if(!n){
59518             return false;
59519         }
59520         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59521         return r ? r.rowIndex : false;
59522     },
59523
59524     findCellIndex : function(node){
59525         var stop = this.el.dom;
59526         while(node && node != stop){
59527             if(this.findRE.test(node.className)){
59528                 return this.getCellIndex(node);
59529             }
59530             node = node.parentNode;
59531         }
59532         return false;
59533     },
59534
59535     getColumnId : function(index){
59536         return this.cm.getColumnId(index);
59537     },
59538
59539     getSplitters : function()
59540     {
59541         if(this.splitterSelector){
59542            return Roo.DomQuery.select(this.splitterSelector);
59543         }else{
59544             return null;
59545       }
59546     },
59547
59548     getSplitter : function(index){
59549         return this.getSplitters()[index];
59550     },
59551
59552     onRowOver : function(e, t){
59553         var row;
59554         if((row = this.findRowIndex(t)) !== false){
59555             this.getRowComposite(row).addClass("x-grid-row-over");
59556         }
59557     },
59558
59559     onRowOut : function(e, t){
59560         var row;
59561         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59562             this.getRowComposite(row).removeClass("x-grid-row-over");
59563         }
59564     },
59565
59566     renderHeaders : function(){
59567         var cm = this.cm;
59568         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59569         var cb = [], lb = [], sb = [], lsb = [], p = {};
59570         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59571             p.cellId = "x-grid-hd-0-" + i;
59572             p.splitId = "x-grid-csplit-0-" + i;
59573             p.id = cm.getColumnId(i);
59574             p.value = cm.getColumnHeader(i) || "";
59575             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59576             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59577             if(!cm.isLocked(i)){
59578                 cb[cb.length] = ct.apply(p);
59579                 sb[sb.length] = st.apply(p);
59580             }else{
59581                 lb[lb.length] = ct.apply(p);
59582                 lsb[lsb.length] = st.apply(p);
59583             }
59584         }
59585         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59586                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59587     },
59588
59589     updateHeaders : function(){
59590         var html = this.renderHeaders();
59591         this.lockedHd.update(html[0]);
59592         this.mainHd.update(html[1]);
59593     },
59594
59595     /**
59596      * Focuses the specified row.
59597      * @param {Number} row The row index
59598      */
59599     focusRow : function(row)
59600     {
59601         //Roo.log('GridView.focusRow');
59602         var x = this.scroller.dom.scrollLeft;
59603         this.focusCell(row, 0, false);
59604         this.scroller.dom.scrollLeft = x;
59605     },
59606
59607     /**
59608      * Focuses the specified cell.
59609      * @param {Number} row The row index
59610      * @param {Number} col The column index
59611      * @param {Boolean} hscroll false to disable horizontal scrolling
59612      */
59613     focusCell : function(row, col, hscroll)
59614     {
59615         //Roo.log('GridView.focusCell');
59616         var el = this.ensureVisible(row, col, hscroll);
59617         this.focusEl.alignTo(el, "tl-tl");
59618         if(Roo.isGecko){
59619             this.focusEl.focus();
59620         }else{
59621             this.focusEl.focus.defer(1, this.focusEl);
59622         }
59623     },
59624
59625     /**
59626      * Scrolls the specified cell into view
59627      * @param {Number} row The row index
59628      * @param {Number} col The column index
59629      * @param {Boolean} hscroll false to disable horizontal scrolling
59630      */
59631     ensureVisible : function(row, col, hscroll)
59632     {
59633         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59634         //return null; //disable for testing.
59635         if(typeof row != "number"){
59636             row = row.rowIndex;
59637         }
59638         if(row < 0 && row >= this.ds.getCount()){
59639             return  null;
59640         }
59641         col = (col !== undefined ? col : 0);
59642         var cm = this.grid.colModel;
59643         while(cm.isHidden(col)){
59644             col++;
59645         }
59646
59647         var el = this.getCell(row, col);
59648         if(!el){
59649             return null;
59650         }
59651         var c = this.scroller.dom;
59652
59653         var ctop = parseInt(el.offsetTop, 10);
59654         var cleft = parseInt(el.offsetLeft, 10);
59655         var cbot = ctop + el.offsetHeight;
59656         var cright = cleft + el.offsetWidth;
59657         
59658         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59659         var stop = parseInt(c.scrollTop, 10);
59660         var sleft = parseInt(c.scrollLeft, 10);
59661         var sbot = stop + ch;
59662         var sright = sleft + c.clientWidth;
59663         /*
59664         Roo.log('GridView.ensureVisible:' +
59665                 ' ctop:' + ctop +
59666                 ' c.clientHeight:' + c.clientHeight +
59667                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59668                 ' stop:' + stop +
59669                 ' cbot:' + cbot +
59670                 ' sbot:' + sbot +
59671                 ' ch:' + ch  
59672                 );
59673         */
59674         if(ctop < stop){
59675             c.scrollTop = ctop;
59676             //Roo.log("set scrolltop to ctop DISABLE?");
59677         }else if(cbot > sbot){
59678             //Roo.log("set scrolltop to cbot-ch");
59679             c.scrollTop = cbot-ch;
59680         }
59681         
59682         if(hscroll !== false){
59683             if(cleft < sleft){
59684                 c.scrollLeft = cleft;
59685             }else if(cright > sright){
59686                 c.scrollLeft = cright-c.clientWidth;
59687             }
59688         }
59689          
59690         return el;
59691     },
59692
59693     updateColumns : function(){
59694         this.grid.stopEditing();
59695         var cm = this.grid.colModel, colIds = this.getColumnIds();
59696         //var totalWidth = cm.getTotalWidth();
59697         var pos = 0;
59698         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59699             //if(cm.isHidden(i)) continue;
59700             var w = cm.getColumnWidth(i);
59701             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59702             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59703         }
59704         this.updateSplitters();
59705     },
59706
59707     generateRules : function(cm){
59708         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59709         Roo.util.CSS.removeStyleSheet(rulesId);
59710         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59711             var cid = cm.getColumnId(i);
59712             var align = '';
59713             if(cm.config[i].align){
59714                 align = 'text-align:'+cm.config[i].align+';';
59715             }
59716             var hidden = '';
59717             if(cm.isHidden(i)){
59718                 hidden = 'display:none;';
59719             }
59720             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59721             ruleBuf.push(
59722                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59723                     this.hdSelector, cid, " {\n", align, width, "}\n",
59724                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59725                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59726         }
59727         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59728     },
59729
59730     updateSplitters : function(){
59731         var cm = this.cm, s = this.getSplitters();
59732         if(s){ // splitters not created yet
59733             var pos = 0, locked = true;
59734             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59735                 if(cm.isHidden(i)) {
59736                     continue;
59737                 }
59738                 var w = cm.getColumnWidth(i); // make sure it's a number
59739                 if(!cm.isLocked(i) && locked){
59740                     pos = 0;
59741                     locked = false;
59742                 }
59743                 pos += w;
59744                 s[i].style.left = (pos-this.splitOffset) + "px";
59745             }
59746         }
59747     },
59748
59749     handleHiddenChange : function(colModel, colIndex, hidden){
59750         if(hidden){
59751             this.hideColumn(colIndex);
59752         }else{
59753             this.unhideColumn(colIndex);
59754         }
59755     },
59756
59757     hideColumn : function(colIndex){
59758         var cid = this.getColumnId(colIndex);
59759         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59760         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59761         if(Roo.isSafari){
59762             this.updateHeaders();
59763         }
59764         this.updateSplitters();
59765         this.layout();
59766     },
59767
59768     unhideColumn : function(colIndex){
59769         var cid = this.getColumnId(colIndex);
59770         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59771         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59772
59773         if(Roo.isSafari){
59774             this.updateHeaders();
59775         }
59776         this.updateSplitters();
59777         this.layout();
59778     },
59779
59780     insertRows : function(dm, firstRow, lastRow, isUpdate){
59781         if(firstRow == 0 && lastRow == dm.getCount()-1){
59782             this.refresh();
59783         }else{
59784             if(!isUpdate){
59785                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59786             }
59787             var s = this.getScrollState();
59788             var markup = this.renderRows(firstRow, lastRow);
59789             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59790             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59791             this.restoreScroll(s);
59792             if(!isUpdate){
59793                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59794                 this.syncRowHeights(firstRow, lastRow);
59795                 this.stripeRows(firstRow);
59796                 this.layout();
59797             }
59798         }
59799     },
59800
59801     bufferRows : function(markup, target, index){
59802         var before = null, trows = target.rows, tbody = target.tBodies[0];
59803         if(index < trows.length){
59804             before = trows[index];
59805         }
59806         var b = document.createElement("div");
59807         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59808         var rows = b.firstChild.rows;
59809         for(var i = 0, len = rows.length; i < len; i++){
59810             if(before){
59811                 tbody.insertBefore(rows[0], before);
59812             }else{
59813                 tbody.appendChild(rows[0]);
59814             }
59815         }
59816         b.innerHTML = "";
59817         b = null;
59818     },
59819
59820     deleteRows : function(dm, firstRow, lastRow){
59821         if(dm.getRowCount()<1){
59822             this.fireEvent("beforerefresh", this);
59823             this.mainBody.update("");
59824             this.lockedBody.update("");
59825             this.fireEvent("refresh", this);
59826         }else{
59827             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59828             var bt = this.getBodyTable();
59829             var tbody = bt.firstChild;
59830             var rows = bt.rows;
59831             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59832                 tbody.removeChild(rows[firstRow]);
59833             }
59834             this.stripeRows(firstRow);
59835             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59836         }
59837     },
59838
59839     updateRows : function(dataSource, firstRow, lastRow){
59840         var s = this.getScrollState();
59841         this.refresh();
59842         this.restoreScroll(s);
59843     },
59844
59845     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59846         if(!noRefresh){
59847            this.refresh();
59848         }
59849         this.updateHeaderSortState();
59850     },
59851
59852     getScrollState : function(){
59853         
59854         var sb = this.scroller.dom;
59855         return {left: sb.scrollLeft, top: sb.scrollTop};
59856     },
59857
59858     stripeRows : function(startRow){
59859         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59860             return;
59861         }
59862         startRow = startRow || 0;
59863         var rows = this.getBodyTable().rows;
59864         var lrows = this.getLockedTable().rows;
59865         var cls = ' x-grid-row-alt ';
59866         for(var i = startRow, len = rows.length; i < len; i++){
59867             var row = rows[i], lrow = lrows[i];
59868             var isAlt = ((i+1) % 2 == 0);
59869             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59870             if(isAlt == hasAlt){
59871                 continue;
59872             }
59873             if(isAlt){
59874                 row.className += " x-grid-row-alt";
59875             }else{
59876                 row.className = row.className.replace("x-grid-row-alt", "");
59877             }
59878             if(lrow){
59879                 lrow.className = row.className;
59880             }
59881         }
59882     },
59883
59884     restoreScroll : function(state){
59885         //Roo.log('GridView.restoreScroll');
59886         var sb = this.scroller.dom;
59887         sb.scrollLeft = state.left;
59888         sb.scrollTop = state.top;
59889         this.syncScroll();
59890     },
59891
59892     syncScroll : function(){
59893         //Roo.log('GridView.syncScroll');
59894         var sb = this.scroller.dom;
59895         var sh = this.mainHd.dom;
59896         var bs = this.mainBody.dom;
59897         var lv = this.lockedBody.dom;
59898         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59899         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59900     },
59901
59902     handleScroll : function(e){
59903         this.syncScroll();
59904         var sb = this.scroller.dom;
59905         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59906         e.stopEvent();
59907     },
59908
59909     handleWheel : function(e){
59910         var d = e.getWheelDelta();
59911         this.scroller.dom.scrollTop -= d*22;
59912         // set this here to prevent jumpy scrolling on large tables
59913         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59914         e.stopEvent();
59915     },
59916
59917     renderRows : function(startRow, endRow){
59918         // pull in all the crap needed to render rows
59919         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59920         var colCount = cm.getColumnCount();
59921
59922         if(ds.getCount() < 1){
59923             return ["", ""];
59924         }
59925
59926         // build a map for all the columns
59927         var cs = [];
59928         for(var i = 0; i < colCount; i++){
59929             var name = cm.getDataIndex(i);
59930             cs[i] = {
59931                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59932                 renderer : cm.getRenderer(i),
59933                 id : cm.getColumnId(i),
59934                 locked : cm.isLocked(i),
59935                 has_editor : cm.isCellEditable(i)
59936             };
59937         }
59938
59939         startRow = startRow || 0;
59940         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59941
59942         // records to render
59943         var rs = ds.getRange(startRow, endRow);
59944
59945         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59946     },
59947
59948     // As much as I hate to duplicate code, this was branched because FireFox really hates
59949     // [].join("") on strings. The performance difference was substantial enough to
59950     // branch this function
59951     doRender : Roo.isGecko ?
59952             function(cs, rs, ds, startRow, colCount, stripe){
59953                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59954                 // buffers
59955                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59956                 
59957                 var hasListener = this.grid.hasListener('rowclass');
59958                 var rowcfg = {};
59959                 for(var j = 0, len = rs.length; j < len; j++){
59960                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59961                     for(var i = 0; i < colCount; i++){
59962                         c = cs[i];
59963                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59964                         p.id = c.id;
59965                         p.css = p.attr = "";
59966                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59967                         if(p.value == undefined || p.value === "") {
59968                             p.value = "&#160;";
59969                         }
59970                         if(c.has_editor){
59971                             p.css += ' x-grid-editable-cell';
59972                         }
59973                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59974                             p.css +=  ' x-grid-dirty-cell';
59975                         }
59976                         var markup = ct.apply(p);
59977                         if(!c.locked){
59978                             cb+= markup;
59979                         }else{
59980                             lcb+= markup;
59981                         }
59982                     }
59983                     var alt = [];
59984                     if(stripe && ((rowIndex+1) % 2 == 0)){
59985                         alt.push("x-grid-row-alt")
59986                     }
59987                     if(r.dirty){
59988                         alt.push(  " x-grid-dirty-row");
59989                     }
59990                     rp.cells = lcb;
59991                     if(this.getRowClass){
59992                         alt.push(this.getRowClass(r, rowIndex));
59993                     }
59994                     if (hasListener) {
59995                         rowcfg = {
59996                              
59997                             record: r,
59998                             rowIndex : rowIndex,
59999                             rowClass : ''
60000                         };
60001                         this.grid.fireEvent('rowclass', this, rowcfg);
60002                         alt.push(rowcfg.rowClass);
60003                     }
60004                     rp.alt = alt.join(" ");
60005                     lbuf+= rt.apply(rp);
60006                     rp.cells = cb;
60007                     buf+=  rt.apply(rp);
60008                 }
60009                 return [lbuf, buf];
60010             } :
60011             function(cs, rs, ds, startRow, colCount, stripe){
60012                 var ts = this.templates, ct = ts.cell, rt = ts.row;
60013                 // buffers
60014                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
60015                 var hasListener = this.grid.hasListener('rowclass');
60016  
60017                 var rowcfg = {};
60018                 for(var j = 0, len = rs.length; j < len; j++){
60019                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
60020                     for(var i = 0; i < colCount; i++){
60021                         c = cs[i];
60022                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
60023                         p.id = c.id;
60024                         p.css = p.attr = "";
60025                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
60026                         if(p.value == undefined || p.value === "") {
60027                             p.value = "&#160;";
60028                         }
60029                         //Roo.log(c);
60030                          if(c.has_editor){
60031                             p.css += ' x-grid-editable-cell';
60032                         }
60033                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
60034                             p.css += ' x-grid-dirty-cell' 
60035                         }
60036                         
60037                         var markup = ct.apply(p);
60038                         if(!c.locked){
60039                             cb[cb.length] = markup;
60040                         }else{
60041                             lcb[lcb.length] = markup;
60042                         }
60043                     }
60044                     var alt = [];
60045                     if(stripe && ((rowIndex+1) % 2 == 0)){
60046                         alt.push( "x-grid-row-alt");
60047                     }
60048                     if(r.dirty){
60049                         alt.push(" x-grid-dirty-row");
60050                     }
60051                     rp.cells = lcb;
60052                     if(this.getRowClass){
60053                         alt.push( this.getRowClass(r, rowIndex));
60054                     }
60055                     if (hasListener) {
60056                         rowcfg = {
60057                              
60058                             record: r,
60059                             rowIndex : rowIndex,
60060                             rowClass : ''
60061                         };
60062                         this.grid.fireEvent('rowclass', this, rowcfg);
60063                         alt.push(rowcfg.rowClass);
60064                     }
60065                     
60066                     rp.alt = alt.join(" ");
60067                     rp.cells = lcb.join("");
60068                     lbuf[lbuf.length] = rt.apply(rp);
60069                     rp.cells = cb.join("");
60070                     buf[buf.length] =  rt.apply(rp);
60071                 }
60072                 return [lbuf.join(""), buf.join("")];
60073             },
60074
60075     renderBody : function(){
60076         var markup = this.renderRows();
60077         var bt = this.templates.body;
60078         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
60079     },
60080
60081     /**
60082      * Refreshes the grid
60083      * @param {Boolean} headersToo
60084      */
60085     refresh : function(headersToo){
60086         this.fireEvent("beforerefresh", this);
60087         this.grid.stopEditing();
60088         var result = this.renderBody();
60089         this.lockedBody.update(result[0]);
60090         this.mainBody.update(result[1]);
60091         if(headersToo === true){
60092             this.updateHeaders();
60093             this.updateColumns();
60094             this.updateSplitters();
60095             this.updateHeaderSortState();
60096         }
60097         this.syncRowHeights();
60098         this.layout();
60099         this.fireEvent("refresh", this);
60100     },
60101
60102     handleColumnMove : function(cm, oldIndex, newIndex){
60103         this.indexMap = null;
60104         var s = this.getScrollState();
60105         this.refresh(true);
60106         this.restoreScroll(s);
60107         this.afterMove(newIndex);
60108     },
60109
60110     afterMove : function(colIndex){
60111         if(this.enableMoveAnim && Roo.enableFx){
60112             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
60113         }
60114         // if multisort - fix sortOrder, and reload..
60115         if (this.grid.dataSource.multiSort) {
60116             // the we can call sort again..
60117             var dm = this.grid.dataSource;
60118             var cm = this.grid.colModel;
60119             var so = [];
60120             for(var i = 0; i < cm.config.length; i++ ) {
60121                 
60122                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
60123                     continue; // dont' bother, it's not in sort list or being set.
60124                 }
60125                 
60126                 so.push(cm.config[i].dataIndex);
60127             };
60128             dm.sortOrder = so;
60129             dm.load(dm.lastOptions);
60130             
60131             
60132         }
60133         
60134     },
60135
60136     updateCell : function(dm, rowIndex, dataIndex){
60137         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
60138         if(typeof colIndex == "undefined"){ // not present in grid
60139             return;
60140         }
60141         var cm = this.grid.colModel;
60142         var cell = this.getCell(rowIndex, colIndex);
60143         var cellText = this.getCellText(rowIndex, colIndex);
60144
60145         var p = {
60146             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
60147             id : cm.getColumnId(colIndex),
60148             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
60149         };
60150         var renderer = cm.getRenderer(colIndex);
60151         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
60152         if(typeof val == "undefined" || val === "") {
60153             val = "&#160;";
60154         }
60155         cellText.innerHTML = val;
60156         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
60157         this.syncRowHeights(rowIndex, rowIndex);
60158     },
60159
60160     calcColumnWidth : function(colIndex, maxRowsToMeasure){
60161         var maxWidth = 0;
60162         if(this.grid.autoSizeHeaders){
60163             var h = this.getHeaderCellMeasure(colIndex);
60164             maxWidth = Math.max(maxWidth, h.scrollWidth);
60165         }
60166         var tb, index;
60167         if(this.cm.isLocked(colIndex)){
60168             tb = this.getLockedTable();
60169             index = colIndex;
60170         }else{
60171             tb = this.getBodyTable();
60172             index = colIndex - this.cm.getLockedCount();
60173         }
60174         if(tb && tb.rows){
60175             var rows = tb.rows;
60176             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
60177             for(var i = 0; i < stopIndex; i++){
60178                 var cell = rows[i].childNodes[index].firstChild;
60179                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
60180             }
60181         }
60182         return maxWidth + /*margin for error in IE*/ 5;
60183     },
60184     /**
60185      * Autofit a column to its content.
60186      * @param {Number} colIndex
60187      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
60188      */
60189      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
60190          if(this.cm.isHidden(colIndex)){
60191              return; // can't calc a hidden column
60192          }
60193         if(forceMinSize){
60194             var cid = this.cm.getColumnId(colIndex);
60195             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
60196            if(this.grid.autoSizeHeaders){
60197                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
60198            }
60199         }
60200         var newWidth = this.calcColumnWidth(colIndex);
60201         this.cm.setColumnWidth(colIndex,
60202             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
60203         if(!suppressEvent){
60204             this.grid.fireEvent("columnresize", colIndex, newWidth);
60205         }
60206     },
60207
60208     /**
60209      * Autofits all columns to their content and then expands to fit any extra space in the grid
60210      */
60211      autoSizeColumns : function(){
60212         var cm = this.grid.colModel;
60213         var colCount = cm.getColumnCount();
60214         for(var i = 0; i < colCount; i++){
60215             this.autoSizeColumn(i, true, true);
60216         }
60217         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
60218             this.fitColumns();
60219         }else{
60220             this.updateColumns();
60221             this.layout();
60222         }
60223     },
60224
60225     /**
60226      * Autofits all columns to the grid's width proportionate with their current size
60227      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
60228      */
60229     fitColumns : function(reserveScrollSpace){
60230         var cm = this.grid.colModel;
60231         var colCount = cm.getColumnCount();
60232         var cols = [];
60233         var width = 0;
60234         var i, w;
60235         for (i = 0; i < colCount; i++){
60236             if(!cm.isHidden(i) && !cm.isFixed(i)){
60237                 w = cm.getColumnWidth(i);
60238                 cols.push(i);
60239                 cols.push(w);
60240                 width += w;
60241             }
60242         }
60243         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
60244         if(reserveScrollSpace){
60245             avail -= 17;
60246         }
60247         var frac = (avail - cm.getTotalWidth())/width;
60248         while (cols.length){
60249             w = cols.pop();
60250             i = cols.pop();
60251             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
60252         }
60253         this.updateColumns();
60254         this.layout();
60255     },
60256
60257     onRowSelect : function(rowIndex){
60258         var row = this.getRowComposite(rowIndex);
60259         row.addClass("x-grid-row-selected");
60260     },
60261
60262     onRowDeselect : function(rowIndex){
60263         var row = this.getRowComposite(rowIndex);
60264         row.removeClass("x-grid-row-selected");
60265     },
60266
60267     onCellSelect : function(row, col){
60268         var cell = this.getCell(row, col);
60269         if(cell){
60270             Roo.fly(cell).addClass("x-grid-cell-selected");
60271         }
60272     },
60273
60274     onCellDeselect : function(row, col){
60275         var cell = this.getCell(row, col);
60276         if(cell){
60277             Roo.fly(cell).removeClass("x-grid-cell-selected");
60278         }
60279     },
60280
60281     updateHeaderSortState : function(){
60282         
60283         // sort state can be single { field: xxx, direction : yyy}
60284         // or   { xxx=>ASC , yyy : DESC ..... }
60285         
60286         var mstate = {};
60287         if (!this.ds.multiSort) { 
60288             var state = this.ds.getSortState();
60289             if(!state){
60290                 return;
60291             }
60292             mstate[state.field] = state.direction;
60293             // FIXME... - this is not used here.. but might be elsewhere..
60294             this.sortState = state;
60295             
60296         } else {
60297             mstate = this.ds.sortToggle;
60298         }
60299         //remove existing sort classes..
60300         
60301         var sc = this.sortClasses;
60302         var hds = this.el.select(this.headerSelector).removeClass(sc);
60303         
60304         for(var f in mstate) {
60305         
60306             var sortColumn = this.cm.findColumnIndex(f);
60307             
60308             if(sortColumn != -1){
60309                 var sortDir = mstate[f];        
60310                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
60311             }
60312         }
60313         
60314          
60315         
60316     },
60317
60318
60319     handleHeaderClick : function(g, index,e){
60320         
60321         Roo.log("header click");
60322         
60323         if (Roo.isTouch) {
60324             // touch events on header are handled by context
60325             this.handleHdCtx(g,index,e);
60326             return;
60327         }
60328         
60329         
60330         if(this.headersDisabled){
60331             return;
60332         }
60333         var dm = g.dataSource, cm = g.colModel;
60334         if(!cm.isSortable(index)){
60335             return;
60336         }
60337         g.stopEditing();
60338         
60339         if (dm.multiSort) {
60340             // update the sortOrder
60341             var so = [];
60342             for(var i = 0; i < cm.config.length; i++ ) {
60343                 
60344                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
60345                     continue; // dont' bother, it's not in sort list or being set.
60346                 }
60347                 
60348                 so.push(cm.config[i].dataIndex);
60349             };
60350             dm.sortOrder = so;
60351         }
60352         
60353         
60354         dm.sort(cm.getDataIndex(index));
60355     },
60356
60357
60358     destroy : function(){
60359         if(this.colMenu){
60360             this.colMenu.removeAll();
60361             Roo.menu.MenuMgr.unregister(this.colMenu);
60362             this.colMenu.getEl().remove();
60363             delete this.colMenu;
60364         }
60365         if(this.hmenu){
60366             this.hmenu.removeAll();
60367             Roo.menu.MenuMgr.unregister(this.hmenu);
60368             this.hmenu.getEl().remove();
60369             delete this.hmenu;
60370         }
60371         if(this.grid.enableColumnMove){
60372             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60373             if(dds){
60374                 for(var dd in dds){
60375                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
60376                         var elid = dds[dd].dragElId;
60377                         dds[dd].unreg();
60378                         Roo.get(elid).remove();
60379                     } else if(dds[dd].config.isTarget){
60380                         dds[dd].proxyTop.remove();
60381                         dds[dd].proxyBottom.remove();
60382                         dds[dd].unreg();
60383                     }
60384                     if(Roo.dd.DDM.locationCache[dd]){
60385                         delete Roo.dd.DDM.locationCache[dd];
60386                     }
60387                 }
60388                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
60389             }
60390         }
60391         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60392         this.bind(null, null);
60393         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60394     },
60395
60396     handleLockChange : function(){
60397         this.refresh(true);
60398     },
60399
60400     onDenyColumnLock : function(){
60401
60402     },
60403
60404     onDenyColumnHide : function(){
60405
60406     },
60407
60408     handleHdMenuClick : function(item){
60409         var index = this.hdCtxIndex;
60410         var cm = this.cm, ds = this.ds;
60411         switch(item.id){
60412             case "asc":
60413                 ds.sort(cm.getDataIndex(index), "ASC");
60414                 break;
60415             case "desc":
60416                 ds.sort(cm.getDataIndex(index), "DESC");
60417                 break;
60418             case "lock":
60419                 var lc = cm.getLockedCount();
60420                 if(cm.getColumnCount(true) <= lc+1){
60421                     this.onDenyColumnLock();
60422                     return;
60423                 }
60424                 if(lc != index){
60425                     cm.setLocked(index, true, true);
60426                     cm.moveColumn(index, lc);
60427                     this.grid.fireEvent("columnmove", index, lc);
60428                 }else{
60429                     cm.setLocked(index, true);
60430                 }
60431             break;
60432             case "unlock":
60433                 var lc = cm.getLockedCount();
60434                 if((lc-1) != index){
60435                     cm.setLocked(index, false, true);
60436                     cm.moveColumn(index, lc-1);
60437                     this.grid.fireEvent("columnmove", index, lc-1);
60438                 }else{
60439                     cm.setLocked(index, false);
60440                 }
60441             break;
60442             case 'wider': // used to expand cols on touch..
60443             case 'narrow':
60444                 var cw = cm.getColumnWidth(index);
60445                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60446                 cw = Math.max(0, cw);
60447                 cw = Math.min(cw,4000);
60448                 cm.setColumnWidth(index, cw);
60449                 break;
60450                 
60451             default:
60452                 index = cm.getIndexById(item.id.substr(4));
60453                 if(index != -1){
60454                     if(item.checked && cm.getColumnCount(true) <= 1){
60455                         this.onDenyColumnHide();
60456                         return false;
60457                     }
60458                     cm.setHidden(index, item.checked);
60459                 }
60460         }
60461         return true;
60462     },
60463
60464     beforeColMenuShow : function(){
60465         var cm = this.cm,  colCount = cm.getColumnCount();
60466         this.colMenu.removeAll();
60467         
60468         var items = [];
60469         for(var i = 0; i < colCount; i++){
60470             items.push({
60471                 id: "col-"+cm.getColumnId(i),
60472                 text: cm.getColumnHeader(i),
60473                 checked: !cm.isHidden(i),
60474                 hideOnClick:false
60475             });
60476         }
60477         
60478         if (this.grid.sortColMenu) {
60479             items.sort(function(a,b) {
60480                 if (a.text == b.text) {
60481                     return 0;
60482                 }
60483                 return a.text.toUpperCase() > b.text.toUpperCase() ? 1 : -1;
60484             });
60485         }
60486         
60487         for(var i = 0; i < colCount; i++){
60488             this.colMenu.add(new Roo.menu.CheckItem(items[i]));
60489         }
60490     },
60491
60492     handleHdCtx : function(g, index, e){
60493         e.stopEvent();
60494         var hd = this.getHeaderCell(index);
60495         this.hdCtxIndex = index;
60496         var ms = this.hmenu.items, cm = this.cm;
60497         ms.get("asc").setDisabled(!cm.isSortable(index));
60498         ms.get("desc").setDisabled(!cm.isSortable(index));
60499         if(this.grid.enableColLock !== false){
60500             ms.get("lock").setDisabled(cm.isLocked(index));
60501             ms.get("unlock").setDisabled(!cm.isLocked(index));
60502         }
60503         this.hmenu.show(hd, "tl-bl");
60504     },
60505
60506     handleHdOver : function(e){
60507         var hd = this.findHeaderCell(e.getTarget());
60508         if(hd && !this.headersDisabled){
60509             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60510                this.fly(hd).addClass("x-grid-hd-over");
60511             }
60512         }
60513     },
60514
60515     handleHdOut : function(e){
60516         var hd = this.findHeaderCell(e.getTarget());
60517         if(hd){
60518             this.fly(hd).removeClass("x-grid-hd-over");
60519         }
60520     },
60521
60522     handleSplitDblClick : function(e, t){
60523         var i = this.getCellIndex(t);
60524         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60525             this.autoSizeColumn(i, true);
60526             this.layout();
60527         }
60528     },
60529
60530     render : function(){
60531
60532         var cm = this.cm;
60533         var colCount = cm.getColumnCount();
60534
60535         if(this.grid.monitorWindowResize === true){
60536             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60537         }
60538         var header = this.renderHeaders();
60539         var body = this.templates.body.apply({rows:""});
60540         var html = this.templates.master.apply({
60541             lockedBody: body,
60542             body: body,
60543             lockedHeader: header[0],
60544             header: header[1]
60545         });
60546
60547         //this.updateColumns();
60548
60549         this.grid.getGridEl().dom.innerHTML = html;
60550
60551         this.initElements();
60552         
60553         // a kludge to fix the random scolling effect in webkit
60554         this.el.on("scroll", function() {
60555             this.el.dom.scrollTop=0; // hopefully not recursive..
60556         },this);
60557
60558         this.scroller.on("scroll", this.handleScroll, this);
60559         this.lockedBody.on("mousewheel", this.handleWheel, this);
60560         this.mainBody.on("mousewheel", this.handleWheel, this);
60561
60562         this.mainHd.on("mouseover", this.handleHdOver, this);
60563         this.mainHd.on("mouseout", this.handleHdOut, this);
60564         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60565                 {delegate: "."+this.splitClass});
60566
60567         this.lockedHd.on("mouseover", this.handleHdOver, this);
60568         this.lockedHd.on("mouseout", this.handleHdOut, this);
60569         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60570                 {delegate: "."+this.splitClass});
60571
60572         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60573             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60574         }
60575
60576         this.updateSplitters();
60577
60578         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60579             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60580             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60581         }
60582
60583         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60584             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60585             this.hmenu.add(
60586                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60587                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60588             );
60589             if(this.grid.enableColLock !== false){
60590                 this.hmenu.add('-',
60591                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60592                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60593                 );
60594             }
60595             if (Roo.isTouch) {
60596                  this.hmenu.add('-',
60597                     {id:"wider", text: this.columnsWiderText},
60598                     {id:"narrow", text: this.columnsNarrowText }
60599                 );
60600                 
60601                  
60602             }
60603             
60604             if(this.grid.enableColumnHide !== false){
60605
60606                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60607                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60608                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60609
60610                 this.hmenu.add('-',
60611                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60612                 );
60613             }
60614             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60615
60616             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60617         }
60618
60619         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60620             this.dd = new Roo.grid.GridDragZone(this.grid, {
60621                 ddGroup : this.grid.ddGroup || 'GridDD'
60622             });
60623             
60624         }
60625
60626         /*
60627         for(var i = 0; i < colCount; i++){
60628             if(cm.isHidden(i)){
60629                 this.hideColumn(i);
60630             }
60631             if(cm.config[i].align){
60632                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60633                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60634             }
60635         }*/
60636         
60637         this.updateHeaderSortState();
60638
60639         this.beforeInitialResize();
60640         this.layout(true);
60641
60642         // two part rendering gives faster view to the user
60643         this.renderPhase2.defer(1, this);
60644     },
60645
60646     renderPhase2 : function(){
60647         // render the rows now
60648         this.refresh();
60649         if(this.grid.autoSizeColumns){
60650             this.autoSizeColumns();
60651         }
60652     },
60653
60654     beforeInitialResize : function(){
60655
60656     },
60657
60658     onColumnSplitterMoved : function(i, w){
60659         this.userResized = true;
60660         var cm = this.grid.colModel;
60661         cm.setColumnWidth(i, w, true);
60662         var cid = cm.getColumnId(i);
60663         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60664         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60665         this.updateSplitters();
60666         this.layout();
60667         this.grid.fireEvent("columnresize", i, w);
60668     },
60669
60670     syncRowHeights : function(startIndex, endIndex){
60671         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60672             startIndex = startIndex || 0;
60673             var mrows = this.getBodyTable().rows;
60674             var lrows = this.getLockedTable().rows;
60675             var len = mrows.length-1;
60676             endIndex = Math.min(endIndex || len, len);
60677             for(var i = startIndex; i <= endIndex; i++){
60678                 var m = mrows[i], l = lrows[i];
60679                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60680                 m.style.height = l.style.height = h + "px";
60681             }
60682         }
60683     },
60684
60685     layout : function(initialRender, is2ndPass)
60686     {
60687         var g = this.grid;
60688         var auto = g.autoHeight;
60689         var scrollOffset = 16;
60690         var c = g.getGridEl(), cm = this.cm,
60691                 expandCol = g.autoExpandColumn,
60692                 gv = this;
60693         //c.beginMeasure();
60694
60695         if(!c.dom.offsetWidth){ // display:none?
60696             if(initialRender){
60697                 this.lockedWrap.show();
60698                 this.mainWrap.show();
60699             }
60700             return;
60701         }
60702
60703         var hasLock = this.cm.isLocked(0);
60704
60705         var tbh = this.headerPanel.getHeight();
60706         var bbh = this.footerPanel.getHeight();
60707
60708         if(auto){
60709             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60710             var newHeight = ch + c.getBorderWidth("tb");
60711             if(g.maxHeight){
60712                 newHeight = Math.min(g.maxHeight, newHeight);
60713             }
60714             c.setHeight(newHeight);
60715         }
60716
60717         if(g.autoWidth){
60718             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60719         }
60720
60721         var s = this.scroller;
60722
60723         var csize = c.getSize(true);
60724
60725         this.el.setSize(csize.width, csize.height);
60726
60727         this.headerPanel.setWidth(csize.width);
60728         this.footerPanel.setWidth(csize.width);
60729
60730         var hdHeight = this.mainHd.getHeight();
60731         var vw = csize.width;
60732         var vh = csize.height - (tbh + bbh);
60733
60734         s.setSize(vw, vh);
60735
60736         var bt = this.getBodyTable();
60737         
60738         if(cm.getLockedCount() == cm.config.length){
60739             bt = this.getLockedTable();
60740         }
60741         
60742         var ltWidth = hasLock ?
60743                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60744
60745         var scrollHeight = bt.offsetHeight;
60746         var scrollWidth = ltWidth + bt.offsetWidth;
60747         var vscroll = false, hscroll = false;
60748
60749         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60750
60751         var lw = this.lockedWrap, mw = this.mainWrap;
60752         var lb = this.lockedBody, mb = this.mainBody;
60753
60754         setTimeout(function(){
60755             var t = s.dom.offsetTop;
60756             var w = s.dom.clientWidth,
60757                 h = s.dom.clientHeight;
60758
60759             lw.setTop(t);
60760             lw.setSize(ltWidth, h);
60761
60762             mw.setLeftTop(ltWidth, t);
60763             mw.setSize(w-ltWidth, h);
60764
60765             lb.setHeight(h-hdHeight);
60766             mb.setHeight(h-hdHeight);
60767
60768             if(is2ndPass !== true && !gv.userResized && expandCol){
60769                 // high speed resize without full column calculation
60770                 
60771                 var ci = cm.getIndexById(expandCol);
60772                 if (ci < 0) {
60773                     ci = cm.findColumnIndex(expandCol);
60774                 }
60775                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60776                 var expandId = cm.getColumnId(ci);
60777                 var  tw = cm.getTotalWidth(false);
60778                 var currentWidth = cm.getColumnWidth(ci);
60779                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60780                 if(currentWidth != cw){
60781                     cm.setColumnWidth(ci, cw, true);
60782                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60783                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60784                     gv.updateSplitters();
60785                     gv.layout(false, true);
60786                 }
60787             }
60788
60789             if(initialRender){
60790                 lw.show();
60791                 mw.show();
60792             }
60793             //c.endMeasure();
60794         }, 10);
60795     },
60796
60797     onWindowResize : function(){
60798         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60799             return;
60800         }
60801         this.layout();
60802     },
60803
60804     appendFooter : function(parentEl){
60805         return null;
60806     },
60807
60808     sortAscText : "Sort Ascending",
60809     sortDescText : "Sort Descending",
60810     lockText : "Lock Column",
60811     unlockText : "Unlock Column",
60812     columnsText : "Columns",
60813  
60814     columnsWiderText : "Wider",
60815     columnsNarrowText : "Thinner"
60816 });
60817
60818
60819 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60820     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60821     this.proxy.el.addClass('x-grid3-col-dd');
60822 };
60823
60824 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60825     handleMouseDown : function(e){
60826
60827     },
60828
60829     callHandleMouseDown : function(e){
60830         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60831     }
60832 });
60833 /*
60834  * Based on:
60835  * Ext JS Library 1.1.1
60836  * Copyright(c) 2006-2007, Ext JS, LLC.
60837  *
60838  * Originally Released Under LGPL - original licence link has changed is not relivant.
60839  *
60840  * Fork - LGPL
60841  * <script type="text/javascript">
60842  */
60843  /**
60844  * @extends Roo.dd.DDProxy
60845  * @class Roo.grid.SplitDragZone
60846  * Support for Column Header resizing
60847  * @constructor
60848  * @param {Object} config
60849  */
60850 // private
60851 // This is a support class used internally by the Grid components
60852 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60853     this.grid = grid;
60854     this.view = grid.getView();
60855     this.proxy = this.view.resizeProxy;
60856     Roo.grid.SplitDragZone.superclass.constructor.call(
60857         this,
60858         hd, // ID
60859         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60860         {  // CONFIG
60861             dragElId : Roo.id(this.proxy.dom),
60862             resizeFrame:false
60863         }
60864     );
60865     
60866     this.setHandleElId(Roo.id(hd));
60867     if (hd2 !== false) {
60868         this.setOuterHandleElId(Roo.id(hd2));
60869     }
60870     
60871     this.scroll = false;
60872 };
60873 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60874     fly: Roo.Element.fly,
60875
60876     b4StartDrag : function(x, y){
60877         this.view.headersDisabled = true;
60878         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60879                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60880         );
60881         this.proxy.setHeight(h);
60882         
60883         // for old system colWidth really stored the actual width?
60884         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60885         // which in reality did not work.. - it worked only for fixed sizes
60886         // for resizable we need to use actual sizes.
60887         var w = this.cm.getColumnWidth(this.cellIndex);
60888         if (!this.view.mainWrap) {
60889             // bootstrap.
60890             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60891         }
60892         
60893         
60894         
60895         // this was w-this.grid.minColumnWidth;
60896         // doesnt really make sense? - w = thie curren width or the rendered one?
60897         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60898         this.resetConstraints();
60899         this.setXConstraint(minw, 1000);
60900         this.setYConstraint(0, 0);
60901         this.minX = x - minw;
60902         this.maxX = x + 1000;
60903         this.startPos = x;
60904         if (!this.view.mainWrap) { // this is Bootstrap code..
60905             this.getDragEl().style.display='block';
60906         }
60907         
60908         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60909     },
60910
60911
60912     handleMouseDown : function(e){
60913         ev = Roo.EventObject.setEvent(e);
60914         var t = this.fly(ev.getTarget());
60915         if(t.hasClass("x-grid-split")){
60916             this.cellIndex = this.view.getCellIndex(t.dom);
60917             this.split = t.dom;
60918             this.cm = this.grid.colModel;
60919             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60920                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60921             }
60922         }
60923     },
60924
60925     endDrag : function(e){
60926         this.view.headersDisabled = false;
60927         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60928         var diff = endX - this.startPos;
60929         // 
60930         var w = this.cm.getColumnWidth(this.cellIndex);
60931         if (!this.view.mainWrap) {
60932             w = 0;
60933         }
60934         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60935     },
60936
60937     autoOffset : function(){
60938         this.setDelta(0,0);
60939     }
60940 });/*
60941  * Based on:
60942  * Ext JS Library 1.1.1
60943  * Copyright(c) 2006-2007, Ext JS, LLC.
60944  *
60945  * Originally Released Under LGPL - original licence link has changed is not relivant.
60946  *
60947  * Fork - LGPL
60948  * <script type="text/javascript">
60949  */
60950  
60951 // private
60952 // This is a support class used internally by the Grid components
60953 Roo.grid.GridDragZone = function(grid, config){
60954     this.view = grid.getView();
60955     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60956     if(this.view.lockedBody){
60957         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60958         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60959     }
60960     this.scroll = false;
60961     this.grid = grid;
60962     this.ddel = document.createElement('div');
60963     this.ddel.className = 'x-grid-dd-wrap';
60964 };
60965
60966 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60967     ddGroup : "GridDD",
60968
60969     getDragData : function(e){
60970         var t = Roo.lib.Event.getTarget(e);
60971         var rowIndex = this.view.findRowIndex(t);
60972         var sm = this.grid.selModel;
60973             
60974         //Roo.log(rowIndex);
60975         
60976         if (sm.getSelectedCell) {
60977             // cell selection..
60978             if (!sm.getSelectedCell()) {
60979                 return false;
60980             }
60981             if (rowIndex != sm.getSelectedCell()[0]) {
60982                 return false;
60983             }
60984         
60985         }
60986         if (sm.getSelections && sm.getSelections().length < 1) {
60987             return false;
60988         }
60989         
60990         
60991         // before it used to all dragging of unseleted... - now we dont do that.
60992         if(rowIndex !== false){
60993             
60994             // if editorgrid.. 
60995             
60996             
60997             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60998                
60999             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
61000               //  
61001             //}
61002             if (e.hasModifier()){
61003                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
61004             }
61005             
61006             Roo.log("getDragData");
61007             
61008             return {
61009                 grid: this.grid,
61010                 ddel: this.ddel,
61011                 rowIndex: rowIndex,
61012                 selections: sm.getSelections ? sm.getSelections() : (
61013                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
61014             };
61015         }
61016         return false;
61017     },
61018     
61019     
61020     onInitDrag : function(e){
61021         var data = this.dragData;
61022         this.ddel.innerHTML = this.grid.getDragDropText();
61023         this.proxy.update(this.ddel);
61024         // fire start drag?
61025     },
61026
61027     afterRepair : function(){
61028         this.dragging = false;
61029     },
61030
61031     getRepairXY : function(e, data){
61032         return false;
61033     },
61034
61035     onEndDrag : function(data, e){
61036         // fire end drag?
61037     },
61038
61039     onValidDrop : function(dd, e, id){
61040         // fire drag drop?
61041         this.hideProxy();
61042     },
61043
61044     beforeInvalidDrop : function(e, id){
61045
61046     }
61047 });/*
61048  * Based on:
61049  * Ext JS Library 1.1.1
61050  * Copyright(c) 2006-2007, Ext JS, LLC.
61051  *
61052  * Originally Released Under LGPL - original licence link has changed is not relivant.
61053  *
61054  * Fork - LGPL
61055  * <script type="text/javascript">
61056  */
61057  
61058
61059 /**
61060  * @class Roo.grid.ColumnModel
61061  * @extends Roo.util.Observable
61062  * This is the default implementation of a ColumnModel used by the Grid. It defines
61063  * the columns in the grid.
61064  * <br>Usage:<br>
61065  <pre><code>
61066  var colModel = new Roo.grid.ColumnModel([
61067         {header: "Ticker", width: 60, sortable: true, locked: true},
61068         {header: "Company Name", width: 150, sortable: true},
61069         {header: "Market Cap.", width: 100, sortable: true},
61070         {header: "$ Sales", width: 100, sortable: true, renderer: money},
61071         {header: "Employees", width: 100, sortable: true, resizable: false}
61072  ]);
61073  </code></pre>
61074  * <p>
61075  
61076  * The config options listed for this class are options which may appear in each
61077  * individual column definition.
61078  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
61079  * @constructor
61080  * @param {Object} config An Array of column config objects. See this class's
61081  * config objects for details.
61082 */
61083 Roo.grid.ColumnModel = function(config){
61084         /**
61085      * The config passed into the constructor
61086      */
61087     this.config = []; //config;
61088     this.lookup = {};
61089
61090     // if no id, create one
61091     // if the column does not have a dataIndex mapping,
61092     // map it to the order it is in the config
61093     for(var i = 0, len = config.length; i < len; i++){
61094         this.addColumn(config[i]);
61095         
61096     }
61097
61098     /**
61099      * The width of columns which have no width specified (defaults to 100)
61100      * @type Number
61101      */
61102     this.defaultWidth = 100;
61103
61104     /**
61105      * Default sortable of columns which have no sortable specified (defaults to false)
61106      * @type Boolean
61107      */
61108     this.defaultSortable = false;
61109
61110     this.addEvents({
61111         /**
61112              * @event widthchange
61113              * Fires when the width of a column changes.
61114              * @param {ColumnModel} this
61115              * @param {Number} columnIndex The column index
61116              * @param {Number} newWidth The new width
61117              */
61118             "widthchange": true,
61119         /**
61120              * @event headerchange
61121              * Fires when the text of a header changes.
61122              * @param {ColumnModel} this
61123              * @param {Number} columnIndex The column index
61124              * @param {Number} newText The new header text
61125              */
61126             "headerchange": true,
61127         /**
61128              * @event hiddenchange
61129              * Fires when a column is hidden or "unhidden".
61130              * @param {ColumnModel} this
61131              * @param {Number} columnIndex The column index
61132              * @param {Boolean} hidden true if hidden, false otherwise
61133              */
61134             "hiddenchange": true,
61135             /**
61136          * @event columnmoved
61137          * Fires when a column is moved.
61138          * @param {ColumnModel} this
61139          * @param {Number} oldIndex
61140          * @param {Number} newIndex
61141          */
61142         "columnmoved" : true,
61143         /**
61144          * @event columlockchange
61145          * Fires when a column's locked state is changed
61146          * @param {ColumnModel} this
61147          * @param {Number} colIndex
61148          * @param {Boolean} locked true if locked
61149          */
61150         "columnlockchange" : true
61151     });
61152     Roo.grid.ColumnModel.superclass.constructor.call(this);
61153 };
61154 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
61155     /**
61156      * @cfg {String} header The header text to display in the Grid view.
61157      */
61158         /**
61159      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
61160      */
61161         /**
61162      * @cfg {String} smHeader Header at Bootsrap Small width
61163      */
61164         /**
61165      * @cfg {String} mdHeader Header at Bootsrap Medium width
61166      */
61167         /**
61168      * @cfg {String} lgHeader Header at Bootsrap Large width
61169      */
61170         /**
61171      * @cfg {String} xlHeader Header at Bootsrap extra Large width
61172      */
61173     /**
61174      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
61175      * {@link Roo.data.Record} definition from which to draw the column's value. If not
61176      * specified, the column's index is used as an index into the Record's data Array.
61177      */
61178     /**
61179      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
61180      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
61181      */
61182     /**
61183      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
61184      * Defaults to the value of the {@link #defaultSortable} property.
61185      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
61186      */
61187     /**
61188      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
61189      */
61190     /**
61191      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
61192      */
61193     /**
61194      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
61195      */
61196     /**
61197      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
61198      */
61199     /**
61200      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
61201      * given the cell's data value. See {@link #setRenderer}. If not specified, the
61202      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
61203      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
61204      */
61205        /**
61206      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
61207      */
61208     /**
61209      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
61210      */
61211     /**
61212      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
61213      */
61214     /**
61215      * @cfg {String} cursor (Optional)
61216      */
61217     /**
61218      * @cfg {String} tooltip (Optional)
61219      */
61220     /**
61221      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
61222      */
61223     /**
61224      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
61225      */
61226     /**
61227      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
61228      */
61229     /**
61230      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
61231      */
61232         /**
61233      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
61234      */
61235     /**
61236      * Returns the id of the column at the specified index.
61237      * @param {Number} index The column index
61238      * @return {String} the id
61239      */
61240     getColumnId : function(index){
61241         return this.config[index].id;
61242     },
61243
61244     /**
61245      * Returns the column for a specified id.
61246      * @param {String} id The column id
61247      * @return {Object} the column
61248      */
61249     getColumnById : function(id){
61250         return this.lookup[id];
61251     },
61252
61253     
61254     /**
61255      * Returns the column Object for a specified dataIndex.
61256      * @param {String} dataIndex The column dataIndex
61257      * @return {Object|Boolean} the column or false if not found
61258      */
61259     getColumnByDataIndex: function(dataIndex){
61260         var index = this.findColumnIndex(dataIndex);
61261         return index > -1 ? this.config[index] : false;
61262     },
61263     
61264     /**
61265      * Returns the index for a specified column id.
61266      * @param {String} id The column id
61267      * @return {Number} the index, or -1 if not found
61268      */
61269     getIndexById : function(id){
61270         for(var i = 0, len = this.config.length; i < len; i++){
61271             if(this.config[i].id == id){
61272                 return i;
61273             }
61274         }
61275         return -1;
61276     },
61277     
61278     /**
61279      * Returns the index for a specified column dataIndex.
61280      * @param {String} dataIndex The column dataIndex
61281      * @return {Number} the index, or -1 if not found
61282      */
61283     
61284     findColumnIndex : function(dataIndex){
61285         for(var i = 0, len = this.config.length; i < len; i++){
61286             if(this.config[i].dataIndex == dataIndex){
61287                 return i;
61288             }
61289         }
61290         return -1;
61291     },
61292     
61293     
61294     moveColumn : function(oldIndex, newIndex){
61295         var c = this.config[oldIndex];
61296         this.config.splice(oldIndex, 1);
61297         this.config.splice(newIndex, 0, c);
61298         this.dataMap = null;
61299         this.fireEvent("columnmoved", this, oldIndex, newIndex);
61300     },
61301
61302     isLocked : function(colIndex){
61303         return this.config[colIndex].locked === true;
61304     },
61305
61306     setLocked : function(colIndex, value, suppressEvent){
61307         if(this.isLocked(colIndex) == value){
61308             return;
61309         }
61310         this.config[colIndex].locked = value;
61311         if(!suppressEvent){
61312             this.fireEvent("columnlockchange", this, colIndex, value);
61313         }
61314     },
61315
61316     getTotalLockedWidth : function(){
61317         var totalWidth = 0;
61318         for(var i = 0; i < this.config.length; i++){
61319             if(this.isLocked(i) && !this.isHidden(i)){
61320                 this.totalWidth += this.getColumnWidth(i);
61321             }
61322         }
61323         return totalWidth;
61324     },
61325
61326     getLockedCount : function(){
61327         for(var i = 0, len = this.config.length; i < len; i++){
61328             if(!this.isLocked(i)){
61329                 return i;
61330             }
61331         }
61332         
61333         return this.config.length;
61334     },
61335
61336     /**
61337      * Returns the number of columns.
61338      * @return {Number}
61339      */
61340     getColumnCount : function(visibleOnly){
61341         if(visibleOnly === true){
61342             var c = 0;
61343             for(var i = 0, len = this.config.length; i < len; i++){
61344                 if(!this.isHidden(i)){
61345                     c++;
61346                 }
61347             }
61348             return c;
61349         }
61350         return this.config.length;
61351     },
61352
61353     /**
61354      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
61355      * @param {Function} fn
61356      * @param {Object} scope (optional)
61357      * @return {Array} result
61358      */
61359     getColumnsBy : function(fn, scope){
61360         var r = [];
61361         for(var i = 0, len = this.config.length; i < len; i++){
61362             var c = this.config[i];
61363             if(fn.call(scope||this, c, i) === true){
61364                 r[r.length] = c;
61365             }
61366         }
61367         return r;
61368     },
61369
61370     /**
61371      * Returns true if the specified column is sortable.
61372      * @param {Number} col The column index
61373      * @return {Boolean}
61374      */
61375     isSortable : function(col){
61376         if(typeof this.config[col].sortable == "undefined"){
61377             return this.defaultSortable;
61378         }
61379         return this.config[col].sortable;
61380     },
61381
61382     /**
61383      * Returns the rendering (formatting) function defined for the column.
61384      * @param {Number} col The column index.
61385      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
61386      */
61387     getRenderer : function(col){
61388         if(!this.config[col].renderer){
61389             return Roo.grid.ColumnModel.defaultRenderer;
61390         }
61391         return this.config[col].renderer;
61392     },
61393
61394     /**
61395      * Sets the rendering (formatting) function for a column.
61396      * @param {Number} col The column index
61397      * @param {Function} fn The function to use to process the cell's raw data
61398      * to return HTML markup for the grid view. The render function is called with
61399      * the following parameters:<ul>
61400      * <li>Data value.</li>
61401      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
61402      * <li>css A CSS style string to apply to the table cell.</li>
61403      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
61404      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61405      * <li>Row index</li>
61406      * <li>Column index</li>
61407      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61408      */
61409     setRenderer : function(col, fn){
61410         this.config[col].renderer = fn;
61411     },
61412
61413     /**
61414      * Returns the width for the specified column.
61415      * @param {Number} col The column index
61416      * @param (optional) {String} gridSize bootstrap width size.
61417      * @return {Number}
61418      */
61419     getColumnWidth : function(col, gridSize)
61420         {
61421                 var cfg = this.config[col];
61422                 
61423                 if (typeof(gridSize) == 'undefined') {
61424                         return cfg.width * 1 || this.defaultWidth;
61425                 }
61426                 if (gridSize === false) { // if we set it..
61427                         return cfg.width || false;
61428                 }
61429                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61430                 
61431                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61432                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61433                                 continue;
61434                         }
61435                         return cfg[ sizes[i] ];
61436                 }
61437                 return 1;
61438                 
61439     },
61440
61441     /**
61442      * Sets the width for a column.
61443      * @param {Number} col The column index
61444      * @param {Number} width The new width
61445      */
61446     setColumnWidth : function(col, width, suppressEvent){
61447         this.config[col].width = width;
61448         this.totalWidth = null;
61449         if(!suppressEvent){
61450              this.fireEvent("widthchange", this, col, width);
61451         }
61452     },
61453
61454     /**
61455      * Returns the total width of all columns.
61456      * @param {Boolean} includeHidden True to include hidden column widths
61457      * @return {Number}
61458      */
61459     getTotalWidth : function(includeHidden){
61460         if(!this.totalWidth){
61461             this.totalWidth = 0;
61462             for(var i = 0, len = this.config.length; i < len; i++){
61463                 if(includeHidden || !this.isHidden(i)){
61464                     this.totalWidth += this.getColumnWidth(i);
61465                 }
61466             }
61467         }
61468         return this.totalWidth;
61469     },
61470
61471     /**
61472      * Returns the header for the specified column.
61473      * @param {Number} col The column index
61474      * @return {String}
61475      */
61476     getColumnHeader : function(col){
61477         return this.config[col].header;
61478     },
61479
61480     /**
61481      * Sets the header for a column.
61482      * @param {Number} col The column index
61483      * @param {String} header The new header
61484      */
61485     setColumnHeader : function(col, header){
61486         this.config[col].header = header;
61487         this.fireEvent("headerchange", this, col, header);
61488     },
61489
61490     /**
61491      * Returns the tooltip for the specified column.
61492      * @param {Number} col The column index
61493      * @return {String}
61494      */
61495     getColumnTooltip : function(col){
61496             return this.config[col].tooltip;
61497     },
61498     /**
61499      * Sets the tooltip for a column.
61500      * @param {Number} col The column index
61501      * @param {String} tooltip The new tooltip
61502      */
61503     setColumnTooltip : function(col, tooltip){
61504             this.config[col].tooltip = tooltip;
61505     },
61506
61507     /**
61508      * Returns the dataIndex for the specified column.
61509      * @param {Number} col The column index
61510      * @return {Number}
61511      */
61512     getDataIndex : function(col){
61513         return this.config[col].dataIndex;
61514     },
61515
61516     /**
61517      * Sets the dataIndex for a column.
61518      * @param {Number} col The column index
61519      * @param {Number} dataIndex The new dataIndex
61520      */
61521     setDataIndex : function(col, dataIndex){
61522         this.config[col].dataIndex = dataIndex;
61523     },
61524
61525     
61526     
61527     /**
61528      * Returns true if the cell is editable.
61529      * @param {Number} colIndex The column index
61530      * @param {Number} rowIndex The row index - this is nto actually used..?
61531      * @return {Boolean}
61532      */
61533     isCellEditable : function(colIndex, rowIndex){
61534         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61535     },
61536
61537     /**
61538      * Returns the editor defined for the cell/column.
61539      * return false or null to disable editing.
61540      * @param {Number} colIndex The column index
61541      * @param {Number} rowIndex The row index
61542      * @return {Object}
61543      */
61544     getCellEditor : function(colIndex, rowIndex){
61545         return this.config[colIndex].editor;
61546     },
61547
61548     /**
61549      * Sets if a column is editable.
61550      * @param {Number} col The column index
61551      * @param {Boolean} editable True if the column is editable
61552      */
61553     setEditable : function(col, editable){
61554         this.config[col].editable = editable;
61555     },
61556
61557
61558     /**
61559      * Returns true if the column is hidden.
61560      * @param {Number} colIndex The column index
61561      * @return {Boolean}
61562      */
61563     isHidden : function(colIndex){
61564         return this.config[colIndex].hidden;
61565     },
61566
61567
61568     /**
61569      * Returns true if the column width cannot be changed
61570      */
61571     isFixed : function(colIndex){
61572         return this.config[colIndex].fixed;
61573     },
61574
61575     /**
61576      * Returns true if the column can be resized
61577      * @return {Boolean}
61578      */
61579     isResizable : function(colIndex){
61580         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61581     },
61582     /**
61583      * Sets if a column is hidden.
61584      * @param {Number} colIndex The column index
61585      * @param {Boolean} hidden True if the column is hidden
61586      */
61587     setHidden : function(colIndex, hidden){
61588         this.config[colIndex].hidden = hidden;
61589         this.totalWidth = null;
61590         this.fireEvent("hiddenchange", this, colIndex, hidden);
61591     },
61592
61593     /**
61594      * Sets the editor for a column.
61595      * @param {Number} col The column index
61596      * @param {Object} editor The editor object
61597      */
61598     setEditor : function(col, editor){
61599         this.config[col].editor = editor;
61600     },
61601     /**
61602      * Add a column (experimental...) - defaults to adding to the end..
61603      * @param {Object} config 
61604     */
61605     addColumn : function(c)
61606     {
61607     
61608         var i = this.config.length;
61609         this.config[i] = c;
61610         
61611         if(typeof c.dataIndex == "undefined"){
61612             c.dataIndex = i;
61613         }
61614         if(typeof c.renderer == "string"){
61615             c.renderer = Roo.util.Format[c.renderer];
61616         }
61617         if(typeof c.id == "undefined"){
61618             c.id = Roo.id();
61619         }
61620         if(c.editor && c.editor.xtype){
61621             c.editor  = Roo.factory(c.editor, Roo.grid);
61622         }
61623         if(c.editor && c.editor.isFormField){
61624             c.editor = new Roo.grid.GridEditor(c.editor);
61625         }
61626         this.lookup[c.id] = c;
61627     }
61628     
61629 });
61630
61631 Roo.grid.ColumnModel.defaultRenderer = function(value)
61632 {
61633     if(typeof value == "object") {
61634         return value;
61635     }
61636         if(typeof value == "string" && value.length < 1){
61637             return "&#160;";
61638         }
61639     
61640         return String.format("{0}", value);
61641 };
61642
61643 // Alias for backwards compatibility
61644 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61645 /*
61646  * Based on:
61647  * Ext JS Library 1.1.1
61648  * Copyright(c) 2006-2007, Ext JS, LLC.
61649  *
61650  * Originally Released Under LGPL - original licence link has changed is not relivant.
61651  *
61652  * Fork - LGPL
61653  * <script type="text/javascript">
61654  */
61655
61656 /**
61657  * @class Roo.grid.AbstractSelectionModel
61658  * @extends Roo.util.Observable
61659  * @abstract
61660  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61661  * implemented by descendant classes.  This class should not be directly instantiated.
61662  * @constructor
61663  */
61664 Roo.grid.AbstractSelectionModel = function(){
61665     this.locked = false;
61666     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61667 };
61668
61669 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61670     /** @ignore Called by the grid automatically. Do not call directly. */
61671     init : function(grid){
61672         this.grid = grid;
61673         this.initEvents();
61674     },
61675
61676     /**
61677      * Locks the selections.
61678      */
61679     lock : function(){
61680         this.locked = true;
61681     },
61682
61683     /**
61684      * Unlocks the selections.
61685      */
61686     unlock : function(){
61687         this.locked = false;
61688     },
61689
61690     /**
61691      * Returns true if the selections are locked.
61692      * @return {Boolean}
61693      */
61694     isLocked : function(){
61695         return this.locked;
61696     }
61697 });/*
61698  * Based on:
61699  * Ext JS Library 1.1.1
61700  * Copyright(c) 2006-2007, Ext JS, LLC.
61701  *
61702  * Originally Released Under LGPL - original licence link has changed is not relivant.
61703  *
61704  * Fork - LGPL
61705  * <script type="text/javascript">
61706  */
61707 /**
61708  * @extends Roo.grid.AbstractSelectionModel
61709  * @class Roo.grid.RowSelectionModel
61710  * The default SelectionModel used by {@link Roo.grid.Grid}.
61711  * It supports multiple selections and keyboard selection/navigation. 
61712  * @constructor
61713  * @param {Object} config
61714  */
61715 Roo.grid.RowSelectionModel = function(config){
61716     Roo.apply(this, config);
61717     this.selections = new Roo.util.MixedCollection(false, function(o){
61718         return o.id;
61719     });
61720
61721     this.last = false;
61722     this.lastActive = false;
61723
61724     this.addEvents({
61725         /**
61726         * @event selectionchange
61727         * Fires when the selection changes
61728         * @param {SelectionModel} this
61729         */
61730        "selectionchange" : true,
61731        /**
61732         * @event afterselectionchange
61733         * Fires after the selection changes (eg. by key press or clicking)
61734         * @param {SelectionModel} this
61735         */
61736        "afterselectionchange" : true,
61737        /**
61738         * @event beforerowselect
61739         * Fires when a row is selected being selected, return false to cancel.
61740         * @param {SelectionModel} this
61741         * @param {Number} rowIndex The selected index
61742         * @param {Boolean} keepExisting False if other selections will be cleared
61743         */
61744        "beforerowselect" : true,
61745        /**
61746         * @event rowselect
61747         * Fires when a row is selected.
61748         * @param {SelectionModel} this
61749         * @param {Number} rowIndex The selected index
61750         * @param {Roo.data.Record} r The record
61751         */
61752        "rowselect" : true,
61753        /**
61754         * @event rowdeselect
61755         * Fires when a row is deselected.
61756         * @param {SelectionModel} this
61757         * @param {Number} rowIndex The selected index
61758         */
61759         "rowdeselect" : true
61760     });
61761     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61762     this.locked = false;
61763 };
61764
61765 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61766     /**
61767      * @cfg {Boolean} singleSelect
61768      * True to allow selection of only one row at a time (defaults to false)
61769      */
61770     singleSelect : false,
61771
61772     // private
61773     initEvents : function(){
61774
61775         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61776             this.grid.on("mousedown", this.handleMouseDown, this);
61777         }else{ // allow click to work like normal
61778             this.grid.on("rowclick", this.handleDragableRowClick, this);
61779         }
61780         // bootstrap does not have a view..
61781         var view = this.grid.view ? this.grid.view : this.grid;
61782         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61783             "up" : function(e){
61784                 if(!e.shiftKey){
61785                     this.selectPrevious(e.shiftKey);
61786                 }else if(this.last !== false && this.lastActive !== false){
61787                     var last = this.last;
61788                     this.selectRange(this.last,  this.lastActive-1);
61789                     view.focusRow(this.lastActive);
61790                     if(last !== false){
61791                         this.last = last;
61792                     }
61793                 }else{
61794                     this.selectFirstRow();
61795                 }
61796                 this.fireEvent("afterselectionchange", this);
61797             },
61798             "down" : function(e){
61799                 if(!e.shiftKey){
61800                     this.selectNext(e.shiftKey);
61801                 }else if(this.last !== false && this.lastActive !== false){
61802                     var last = this.last;
61803                     this.selectRange(this.last,  this.lastActive+1);
61804                     view.focusRow(this.lastActive);
61805                     if(last !== false){
61806                         this.last = last;
61807                     }
61808                 }else{
61809                     this.selectFirstRow();
61810                 }
61811                 this.fireEvent("afterselectionchange", this);
61812             },
61813             scope: this
61814         });
61815
61816          
61817         view.on("refresh", this.onRefresh, this);
61818         view.on("rowupdated", this.onRowUpdated, this);
61819         view.on("rowremoved", this.onRemove, this);
61820     },
61821
61822     // private
61823     onRefresh : function(){
61824         var ds = this.grid.ds, i, v = this.grid.view;
61825         var s = this.selections;
61826         s.each(function(r){
61827             if((i = ds.indexOfId(r.id)) != -1){
61828                 v.onRowSelect(i);
61829                 s.add(ds.getAt(i)); // updating the selection relate data
61830             }else{
61831                 s.remove(r);
61832             }
61833         });
61834     },
61835
61836     // private
61837     onRemove : function(v, index, r){
61838         this.selections.remove(r);
61839     },
61840
61841     // private
61842     onRowUpdated : function(v, index, r){
61843         if(this.isSelected(r)){
61844             v.onRowSelect(index);
61845         }
61846     },
61847
61848     /**
61849      * Select records.
61850      * @param {Array} records The records to select
61851      * @param {Boolean} keepExisting (optional) True to keep existing selections
61852      */
61853     selectRecords : function(records, keepExisting){
61854         if(!keepExisting){
61855             this.clearSelections();
61856         }
61857         var ds = this.grid.ds;
61858         for(var i = 0, len = records.length; i < len; i++){
61859             this.selectRow(ds.indexOf(records[i]), true);
61860         }
61861     },
61862
61863     /**
61864      * Gets the number of selected rows.
61865      * @return {Number}
61866      */
61867     getCount : function(){
61868         return this.selections.length;
61869     },
61870
61871     /**
61872      * Selects the first row in the grid.
61873      */
61874     selectFirstRow : function(){
61875         this.selectRow(0);
61876     },
61877
61878     /**
61879      * Select the last row.
61880      * @param {Boolean} keepExisting (optional) True to keep existing selections
61881      */
61882     selectLastRow : function(keepExisting){
61883         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61884     },
61885
61886     /**
61887      * Selects the row immediately following the last selected row.
61888      * @param {Boolean} keepExisting (optional) True to keep existing selections
61889      */
61890     selectNext : function(keepExisting){
61891         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61892             this.selectRow(this.last+1, keepExisting);
61893             var view = this.grid.view ? this.grid.view : this.grid;
61894             view.focusRow(this.last);
61895         }
61896     },
61897
61898     /**
61899      * Selects the row that precedes the last selected row.
61900      * @param {Boolean} keepExisting (optional) True to keep existing selections
61901      */
61902     selectPrevious : function(keepExisting){
61903         if(this.last){
61904             this.selectRow(this.last-1, keepExisting);
61905             var view = this.grid.view ? this.grid.view : this.grid;
61906             view.focusRow(this.last);
61907         }
61908     },
61909
61910     /**
61911      * Returns the selected records
61912      * @return {Array} Array of selected records
61913      */
61914     getSelections : function(){
61915         return [].concat(this.selections.items);
61916     },
61917
61918     /**
61919      * Returns the first selected record.
61920      * @return {Record}
61921      */
61922     getSelected : function(){
61923         return this.selections.itemAt(0);
61924     },
61925
61926
61927     /**
61928      * Clears all selections.
61929      */
61930     clearSelections : function(fast){
61931         if(this.locked) {
61932             return;
61933         }
61934         if(fast !== true){
61935             var ds = this.grid.ds;
61936             var s = this.selections;
61937             s.each(function(r){
61938                 this.deselectRow(ds.indexOfId(r.id));
61939             }, this);
61940             s.clear();
61941         }else{
61942             this.selections.clear();
61943         }
61944         this.last = false;
61945     },
61946
61947
61948     /**
61949      * Selects all rows.
61950      */
61951     selectAll : function(){
61952         if(this.locked) {
61953             return;
61954         }
61955         this.selections.clear();
61956         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61957             this.selectRow(i, true);
61958         }
61959     },
61960
61961     /**
61962      * Returns True if there is a selection.
61963      * @return {Boolean}
61964      */
61965     hasSelection : function(){
61966         return this.selections.length > 0;
61967     },
61968
61969     /**
61970      * Returns True if the specified row is selected.
61971      * @param {Number/Record} record The record or index of the record to check
61972      * @return {Boolean}
61973      */
61974     isSelected : function(index){
61975         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61976         return (r && this.selections.key(r.id) ? true : false);
61977     },
61978
61979     /**
61980      * Returns True if the specified record id is selected.
61981      * @param {String} id The id of record to check
61982      * @return {Boolean}
61983      */
61984     isIdSelected : function(id){
61985         return (this.selections.key(id) ? true : false);
61986     },
61987
61988     // private
61989     handleMouseDown : function(e, t)
61990     {
61991         var view = this.grid.view ? this.grid.view : this.grid;
61992         var rowIndex;
61993         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61994             return;
61995         };
61996         if(e.shiftKey && this.last !== false){
61997             var last = this.last;
61998             this.selectRange(last, rowIndex, e.ctrlKey);
61999             this.last = last; // reset the last
62000             view.focusRow(rowIndex);
62001         }else{
62002             var isSelected = this.isSelected(rowIndex);
62003             if(e.button !== 0 && isSelected){
62004                 view.focusRow(rowIndex);
62005             }else if(e.ctrlKey && isSelected){
62006                 this.deselectRow(rowIndex);
62007             }else if(!isSelected){
62008                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
62009                 view.focusRow(rowIndex);
62010             }
62011         }
62012         this.fireEvent("afterselectionchange", this);
62013     },
62014     // private
62015     handleDragableRowClick :  function(grid, rowIndex, e) 
62016     {
62017         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
62018             this.selectRow(rowIndex, false);
62019             var view = this.grid.view ? this.grid.view : this.grid;
62020             view.focusRow(rowIndex);
62021              this.fireEvent("afterselectionchange", this);
62022         }
62023     },
62024     
62025     /**
62026      * Selects multiple rows.
62027      * @param {Array} rows Array of the indexes of the row to select
62028      * @param {Boolean} keepExisting (optional) True to keep existing selections
62029      */
62030     selectRows : function(rows, keepExisting){
62031         if(!keepExisting){
62032             this.clearSelections();
62033         }
62034         for(var i = 0, len = rows.length; i < len; i++){
62035             this.selectRow(rows[i], true);
62036         }
62037     },
62038
62039     /**
62040      * Selects a range of rows. All rows in between startRow and endRow are also selected.
62041      * @param {Number} startRow The index of the first row in the range
62042      * @param {Number} endRow The index of the last row in the range
62043      * @param {Boolean} keepExisting (optional) True to retain existing selections
62044      */
62045     selectRange : function(startRow, endRow, keepExisting){
62046         if(this.locked) {
62047             return;
62048         }
62049         if(!keepExisting){
62050             this.clearSelections();
62051         }
62052         if(startRow <= endRow){
62053             for(var i = startRow; i <= endRow; i++){
62054                 this.selectRow(i, true);
62055             }
62056         }else{
62057             for(var i = startRow; i >= endRow; i--){
62058                 this.selectRow(i, true);
62059             }
62060         }
62061     },
62062
62063     /**
62064      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
62065      * @param {Number} startRow The index of the first row in the range
62066      * @param {Number} endRow The index of the last row in the range
62067      */
62068     deselectRange : function(startRow, endRow, preventViewNotify){
62069         if(this.locked) {
62070             return;
62071         }
62072         for(var i = startRow; i <= endRow; i++){
62073             this.deselectRow(i, preventViewNotify);
62074         }
62075     },
62076
62077     /**
62078      * Selects a row.
62079      * @param {Number} row The index of the row to select
62080      * @param {Boolean} keepExisting (optional) True to keep existing selections
62081      */
62082     selectRow : function(index, keepExisting, preventViewNotify){
62083         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
62084             return;
62085         }
62086         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
62087             if(!keepExisting || this.singleSelect){
62088                 this.clearSelections();
62089             }
62090             var r = this.grid.ds.getAt(index);
62091             this.selections.add(r);
62092             this.last = this.lastActive = index;
62093             if(!preventViewNotify){
62094                 var view = this.grid.view ? this.grid.view : this.grid;
62095                 view.onRowSelect(index);
62096             }
62097             this.fireEvent("rowselect", this, index, r);
62098             this.fireEvent("selectionchange", this);
62099         }
62100     },
62101
62102     /**
62103      * Deselects a row.
62104      * @param {Number} row The index of the row to deselect
62105      */
62106     deselectRow : function(index, preventViewNotify){
62107         if(this.locked) {
62108             return;
62109         }
62110         if(this.last == index){
62111             this.last = false;
62112         }
62113         if(this.lastActive == index){
62114             this.lastActive = false;
62115         }
62116         var r = this.grid.ds.getAt(index);
62117         this.selections.remove(r);
62118         if(!preventViewNotify){
62119             var view = this.grid.view ? this.grid.view : this.grid;
62120             view.onRowDeselect(index);
62121         }
62122         this.fireEvent("rowdeselect", this, index);
62123         this.fireEvent("selectionchange", this);
62124     },
62125
62126     // private
62127     restoreLast : function(){
62128         if(this._last){
62129             this.last = this._last;
62130         }
62131     },
62132
62133     // private
62134     acceptsNav : function(row, col, cm){
62135         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62136     },
62137
62138     // private
62139     onEditorKey : function(field, e){
62140         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
62141         if(k == e.TAB){
62142             e.stopEvent();
62143             ed.completeEdit();
62144             if(e.shiftKey){
62145                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62146             }else{
62147                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62148             }
62149         }else if(k == e.ENTER && !e.ctrlKey){
62150             e.stopEvent();
62151             ed.completeEdit();
62152             if(e.shiftKey){
62153                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
62154             }else{
62155                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
62156             }
62157         }else if(k == e.ESC){
62158             ed.cancelEdit();
62159         }
62160         if(newCell){
62161             g.startEditing(newCell[0], newCell[1]);
62162         }
62163     }
62164 });/*
62165  * Based on:
62166  * Ext JS Library 1.1.1
62167  * Copyright(c) 2006-2007, Ext JS, LLC.
62168  *
62169  * Originally Released Under LGPL - original licence link has changed is not relivant.
62170  *
62171  * Fork - LGPL
62172  * <script type="text/javascript">
62173  */
62174 /**
62175  * @class Roo.grid.CellSelectionModel
62176  * @extends Roo.grid.AbstractSelectionModel
62177  * This class provides the basic implementation for cell selection in a grid.
62178  * @constructor
62179  * @param {Object} config The object containing the configuration of this model.
62180  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
62181  */
62182 Roo.grid.CellSelectionModel = function(config){
62183     Roo.apply(this, config);
62184
62185     this.selection = null;
62186
62187     this.addEvents({
62188         /**
62189              * @event beforerowselect
62190              * Fires before a cell is selected.
62191              * @param {SelectionModel} this
62192              * @param {Number} rowIndex The selected row index
62193              * @param {Number} colIndex The selected cell index
62194              */
62195             "beforecellselect" : true,
62196         /**
62197              * @event cellselect
62198              * Fires when a cell is selected.
62199              * @param {SelectionModel} this
62200              * @param {Number} rowIndex The selected row index
62201              * @param {Number} colIndex The selected cell index
62202              */
62203             "cellselect" : true,
62204         /**
62205              * @event selectionchange
62206              * Fires when the active selection changes.
62207              * @param {SelectionModel} this
62208              * @param {Object} selection null for no selection or an object (o) with two properties
62209                 <ul>
62210                 <li>o.record: the record object for the row the selection is in</li>
62211                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
62212                 </ul>
62213              */
62214             "selectionchange" : true,
62215         /**
62216              * @event tabend
62217              * Fires when the tab (or enter) was pressed on the last editable cell
62218              * You can use this to trigger add new row.
62219              * @param {SelectionModel} this
62220              */
62221             "tabend" : true,
62222          /**
62223              * @event beforeeditnext
62224              * Fires before the next editable sell is made active
62225              * You can use this to skip to another cell or fire the tabend
62226              *    if you set cell to false
62227              * @param {Object} eventdata object : { cell : [ row, col ] } 
62228              */
62229             "beforeeditnext" : true
62230     });
62231     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
62232 };
62233
62234 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
62235     
62236     enter_is_tab: false,
62237
62238     /** @ignore */
62239     initEvents : function(){
62240         this.grid.on("mousedown", this.handleMouseDown, this);
62241         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
62242         var view = this.grid.view;
62243         view.on("refresh", this.onViewChange, this);
62244         view.on("rowupdated", this.onRowUpdated, this);
62245         view.on("beforerowremoved", this.clearSelections, this);
62246         view.on("beforerowsinserted", this.clearSelections, this);
62247         if(this.grid.isEditor){
62248             this.grid.on("beforeedit", this.beforeEdit,  this);
62249         }
62250     },
62251
62252         //private
62253     beforeEdit : function(e){
62254         this.select(e.row, e.column, false, true, e.record);
62255     },
62256
62257         //private
62258     onRowUpdated : function(v, index, r){
62259         if(this.selection && this.selection.record == r){
62260             v.onCellSelect(index, this.selection.cell[1]);
62261         }
62262     },
62263
62264         //private
62265     onViewChange : function(){
62266         this.clearSelections(true);
62267     },
62268
62269         /**
62270          * Returns the currently selected cell,.
62271          * @return {Array} The selected cell (row, column) or null if none selected.
62272          */
62273     getSelectedCell : function(){
62274         return this.selection ? this.selection.cell : null;
62275     },
62276
62277     /**
62278      * Clears all selections.
62279      * @param {Boolean} true to prevent the gridview from being notified about the change.
62280      */
62281     clearSelections : function(preventNotify){
62282         var s = this.selection;
62283         if(s){
62284             if(preventNotify !== true){
62285                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
62286             }
62287             this.selection = null;
62288             this.fireEvent("selectionchange", this, null);
62289         }
62290     },
62291
62292     /**
62293      * Returns true if there is a selection.
62294      * @return {Boolean}
62295      */
62296     hasSelection : function(){
62297         return this.selection ? true : false;
62298     },
62299
62300     /** @ignore */
62301     handleMouseDown : function(e, t){
62302         var v = this.grid.getView();
62303         if(this.isLocked()){
62304             return;
62305         };
62306         var row = v.findRowIndex(t);
62307         var cell = v.findCellIndex(t);
62308         if(row !== false && cell !== false){
62309             this.select(row, cell);
62310         }
62311     },
62312
62313     /**
62314      * Selects a cell.
62315      * @param {Number} rowIndex
62316      * @param {Number} collIndex
62317      */
62318     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
62319         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
62320             this.clearSelections();
62321             r = r || this.grid.dataSource.getAt(rowIndex);
62322             this.selection = {
62323                 record : r,
62324                 cell : [rowIndex, colIndex]
62325             };
62326             if(!preventViewNotify){
62327                 var v = this.grid.getView();
62328                 v.onCellSelect(rowIndex, colIndex);
62329                 if(preventFocus !== true){
62330                     v.focusCell(rowIndex, colIndex);
62331                 }
62332             }
62333             this.fireEvent("cellselect", this, rowIndex, colIndex);
62334             this.fireEvent("selectionchange", this, this.selection);
62335         }
62336     },
62337
62338         //private
62339     isSelectable : function(rowIndex, colIndex, cm){
62340         return !cm.isHidden(colIndex);
62341     },
62342
62343     /** @ignore */
62344     handleKeyDown : function(e){
62345         //Roo.log('Cell Sel Model handleKeyDown');
62346         if(!e.isNavKeyPress()){
62347             return;
62348         }
62349         var g = this.grid, s = this.selection;
62350         if(!s){
62351             e.stopEvent();
62352             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
62353             if(cell){
62354                 this.select(cell[0], cell[1]);
62355             }
62356             return;
62357         }
62358         var sm = this;
62359         var walk = function(row, col, step){
62360             return g.walkCells(row, col, step, sm.isSelectable,  sm);
62361         };
62362         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
62363         var newCell;
62364
62365       
62366
62367         switch(k){
62368             case e.TAB:
62369                 // handled by onEditorKey
62370                 if (g.isEditor && g.editing) {
62371                     return;
62372                 }
62373                 if(e.shiftKey) {
62374                     newCell = walk(r, c-1, -1);
62375                 } else {
62376                     newCell = walk(r, c+1, 1);
62377                 }
62378                 break;
62379             
62380             case e.DOWN:
62381                newCell = walk(r+1, c, 1);
62382                 break;
62383             
62384             case e.UP:
62385                 newCell = walk(r-1, c, -1);
62386                 break;
62387             
62388             case e.RIGHT:
62389                 newCell = walk(r, c+1, 1);
62390                 break;
62391             
62392             case e.LEFT:
62393                 newCell = walk(r, c-1, -1);
62394                 break;
62395             
62396             case e.ENTER:
62397                 
62398                 if(g.isEditor && !g.editing){
62399                    g.startEditing(r, c);
62400                    e.stopEvent();
62401                    return;
62402                 }
62403                 
62404                 
62405              break;
62406         };
62407         if(newCell){
62408             this.select(newCell[0], newCell[1]);
62409             e.stopEvent();
62410             
62411         }
62412     },
62413
62414     acceptsNav : function(row, col, cm){
62415         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62416     },
62417     /**
62418      * Selects a cell.
62419      * @param {Number} field (not used) - as it's normally used as a listener
62420      * @param {Number} e - event - fake it by using
62421      *
62422      * var e = Roo.EventObjectImpl.prototype;
62423      * e.keyCode = e.TAB
62424      *
62425      * 
62426      */
62427     onEditorKey : function(field, e){
62428         
62429         var k = e.getKey(),
62430             newCell,
62431             g = this.grid,
62432             ed = g.activeEditor,
62433             forward = false;
62434         ///Roo.log('onEditorKey' + k);
62435         
62436         
62437         if (this.enter_is_tab && k == e.ENTER) {
62438             k = e.TAB;
62439         }
62440         
62441         if(k == e.TAB){
62442             if(e.shiftKey){
62443                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62444             }else{
62445                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62446                 forward = true;
62447             }
62448             
62449             e.stopEvent();
62450             
62451         } else if(k == e.ENTER &&  !e.ctrlKey){
62452             ed.completeEdit();
62453             e.stopEvent();
62454             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62455         
62456                 } else if(k == e.ESC){
62457             ed.cancelEdit();
62458         }
62459                 
62460         if (newCell) {
62461             var ecall = { cell : newCell, forward : forward };
62462             this.fireEvent('beforeeditnext', ecall );
62463             newCell = ecall.cell;
62464                         forward = ecall.forward;
62465         }
62466                 
62467         if(newCell){
62468             //Roo.log('next cell after edit');
62469             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62470         } else if (forward) {
62471             // tabbed past last
62472             this.fireEvent.defer(100, this, ['tabend',this]);
62473         }
62474     }
62475 });/*
62476  * Based on:
62477  * Ext JS Library 1.1.1
62478  * Copyright(c) 2006-2007, Ext JS, LLC.
62479  *
62480  * Originally Released Under LGPL - original licence link has changed is not relivant.
62481  *
62482  * Fork - LGPL
62483  * <script type="text/javascript">
62484  */
62485  
62486 /**
62487  * @class Roo.grid.EditorGrid
62488  * @extends Roo.grid.Grid
62489  * Class for creating and editable grid.
62490  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62491  * The container MUST have some type of size defined for the grid to fill. The container will be 
62492  * automatically set to position relative if it isn't already.
62493  * @param {Object} dataSource The data model to bind to
62494  * @param {Object} colModel The column model with info about this grid's columns
62495  */
62496 Roo.grid.EditorGrid = function(container, config){
62497     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62498     this.getGridEl().addClass("xedit-grid");
62499
62500     if(!this.selModel){
62501         this.selModel = new Roo.grid.CellSelectionModel();
62502     }
62503
62504     this.activeEditor = null;
62505
62506         this.addEvents({
62507             /**
62508              * @event beforeedit
62509              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62510              * <ul style="padding:5px;padding-left:16px;">
62511              * <li>grid - This grid</li>
62512              * <li>record - The record being edited</li>
62513              * <li>field - The field name being edited</li>
62514              * <li>value - The value for the field being edited.</li>
62515              * <li>row - The grid row index</li>
62516              * <li>column - The grid column index</li>
62517              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62518              * </ul>
62519              * @param {Object} e An edit event (see above for description)
62520              */
62521             "beforeedit" : true,
62522             /**
62523              * @event afteredit
62524              * Fires after a cell is edited. <br />
62525              * <ul style="padding:5px;padding-left:16px;">
62526              * <li>grid - This grid</li>
62527              * <li>record - The record being edited</li>
62528              * <li>field - The field name being edited</li>
62529              * <li>value - The value being set</li>
62530              * <li>originalValue - The original value for the field, before the edit.</li>
62531              * <li>row - The grid row index</li>
62532              * <li>column - The grid column index</li>
62533              * </ul>
62534              * @param {Object} e An edit event (see above for description)
62535              */
62536             "afteredit" : true,
62537             /**
62538              * @event validateedit
62539              * Fires after a cell is edited, but before the value is set in the record. 
62540          * You can use this to modify the value being set in the field, Return false
62541              * to cancel the change. The edit event object has the following properties <br />
62542              * <ul style="padding:5px;padding-left:16px;">
62543          * <li>editor - This editor</li>
62544              * <li>grid - This grid</li>
62545              * <li>record - The record being edited</li>
62546              * <li>field - The field name being edited</li>
62547              * <li>value - The value being set</li>
62548              * <li>originalValue - The original value for the field, before the edit.</li>
62549              * <li>row - The grid row index</li>
62550              * <li>column - The grid column index</li>
62551              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62552              * </ul>
62553              * @param {Object} e An edit event (see above for description)
62554              */
62555             "validateedit" : true
62556         });
62557     this.on("bodyscroll", this.stopEditing,  this);
62558     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62559 };
62560
62561 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62562     /**
62563      * @cfg {Number} clicksToEdit
62564      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62565      */
62566     clicksToEdit: 2,
62567
62568     // private
62569     isEditor : true,
62570     // private
62571     trackMouseOver: false, // causes very odd FF errors
62572
62573     onCellDblClick : function(g, row, col){
62574         this.startEditing(row, col);
62575     },
62576
62577     onEditComplete : function(ed, value, startValue){
62578         this.editing = false;
62579         this.activeEditor = null;
62580         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62581         var r = ed.record;
62582         var field = this.colModel.getDataIndex(ed.col);
62583         var e = {
62584             grid: this,
62585             record: r,
62586             field: field,
62587             originalValue: startValue,
62588             value: value,
62589             row: ed.row,
62590             column: ed.col,
62591             cancel:false,
62592             editor: ed
62593         };
62594         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62595         cell.show();
62596           
62597         if(String(value) !== String(startValue)){
62598             
62599             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62600                 r.set(field, e.value);
62601                 // if we are dealing with a combo box..
62602                 // then we also set the 'name' colum to be the displayField
62603                 if (ed.field.displayField && ed.field.name) {
62604                     r.set(ed.field.name, ed.field.el.dom.value);
62605                 }
62606                 
62607                 delete e.cancel; //?? why!!!
62608                 this.fireEvent("afteredit", e);
62609             }
62610         } else {
62611             this.fireEvent("afteredit", e); // always fire it!
62612         }
62613         this.view.focusCell(ed.row, ed.col);
62614     },
62615
62616     /**
62617      * Starts editing the specified for the specified row/column
62618      * @param {Number} rowIndex
62619      * @param {Number} colIndex
62620      */
62621     startEditing : function(row, col){
62622         this.stopEditing();
62623         if(this.colModel.isCellEditable(col, row)){
62624             this.view.ensureVisible(row, col, true);
62625           
62626             var r = this.dataSource.getAt(row);
62627             var field = this.colModel.getDataIndex(col);
62628             var cell = Roo.get(this.view.getCell(row,col));
62629             var e = {
62630                 grid: this,
62631                 record: r,
62632                 field: field,
62633                 value: r.data[field],
62634                 row: row,
62635                 column: col,
62636                 cancel:false 
62637             };
62638             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62639                 this.editing = true;
62640                 var ed = this.colModel.getCellEditor(col, row);
62641                 
62642                 if (!ed) {
62643                     return;
62644                 }
62645                 if(!ed.rendered){
62646                     ed.render(ed.parentEl || document.body);
62647                 }
62648                 ed.field.reset();
62649                
62650                 cell.hide();
62651                 
62652                 (function(){ // complex but required for focus issues in safari, ie and opera
62653                     ed.row = row;
62654                     ed.col = col;
62655                     ed.record = r;
62656                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62657                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62658                     this.activeEditor = ed;
62659                     var v = r.data[field];
62660                     ed.startEdit(this.view.getCell(row, col), v);
62661                     // combo's with 'displayField and name set
62662                     if (ed.field.displayField && ed.field.name) {
62663                         ed.field.el.dom.value = r.data[ed.field.name];
62664                     }
62665                     
62666                     
62667                 }).defer(50, this);
62668             }
62669         }
62670     },
62671         
62672     /**
62673      * Stops any active editing
62674      */
62675     stopEditing : function(){
62676         if(this.activeEditor){
62677             this.activeEditor.completeEdit();
62678         }
62679         this.activeEditor = null;
62680     },
62681         
62682          /**
62683      * Called to get grid's drag proxy text, by default returns this.ddText.
62684      * @return {String}
62685      */
62686     getDragDropText : function(){
62687         var count = this.selModel.getSelectedCell() ? 1 : 0;
62688         return String.format(this.ddText, count, count == 1 ? '' : 's');
62689     }
62690         
62691 });/*
62692  * Based on:
62693  * Ext JS Library 1.1.1
62694  * Copyright(c) 2006-2007, Ext JS, LLC.
62695  *
62696  * Originally Released Under LGPL - original licence link has changed is not relivant.
62697  *
62698  * Fork - LGPL
62699  * <script type="text/javascript">
62700  */
62701
62702 // private - not really -- you end up using it !
62703 // This is a support class used internally by the Grid components
62704
62705 /**
62706  * @class Roo.grid.GridEditor
62707  * @extends Roo.Editor
62708  * Class for creating and editable grid elements.
62709  * @param {Object} config any settings (must include field)
62710  */
62711 Roo.grid.GridEditor = function(field, config){
62712     if (!config && field.field) {
62713         config = field;
62714         field = Roo.factory(config.field, Roo.form);
62715     }
62716     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62717     field.monitorTab = false;
62718 };
62719
62720 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62721     
62722     /**
62723      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62724      */
62725     
62726     alignment: "tl-tl",
62727     autoSize: "width",
62728     hideEl : false,
62729     cls: "x-small-editor x-grid-editor",
62730     shim:false,
62731     shadow:"frame"
62732 });/*
62733  * Based on:
62734  * Ext JS Library 1.1.1
62735  * Copyright(c) 2006-2007, Ext JS, LLC.
62736  *
62737  * Originally Released Under LGPL - original licence link has changed is not relivant.
62738  *
62739  * Fork - LGPL
62740  * <script type="text/javascript">
62741  */
62742   
62743
62744   
62745 Roo.grid.PropertyRecord = Roo.data.Record.create([
62746     {name:'name',type:'string'},  'value'
62747 ]);
62748
62749
62750 Roo.grid.PropertyStore = function(grid, source){
62751     this.grid = grid;
62752     this.store = new Roo.data.Store({
62753         recordType : Roo.grid.PropertyRecord
62754     });
62755     this.store.on('update', this.onUpdate,  this);
62756     if(source){
62757         this.setSource(source);
62758     }
62759     Roo.grid.PropertyStore.superclass.constructor.call(this);
62760 };
62761
62762
62763
62764 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62765     setSource : function(o){
62766         this.source = o;
62767         this.store.removeAll();
62768         var data = [];
62769         for(var k in o){
62770             if(this.isEditableValue(o[k])){
62771                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62772             }
62773         }
62774         this.store.loadRecords({records: data}, {}, true);
62775     },
62776
62777     onUpdate : function(ds, record, type){
62778         if(type == Roo.data.Record.EDIT){
62779             var v = record.data['value'];
62780             var oldValue = record.modified['value'];
62781             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62782                 this.source[record.id] = v;
62783                 record.commit();
62784                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62785             }else{
62786                 record.reject();
62787             }
62788         }
62789     },
62790
62791     getProperty : function(row){
62792        return this.store.getAt(row);
62793     },
62794
62795     isEditableValue: function(val){
62796         if(val && val instanceof Date){
62797             return true;
62798         }else if(typeof val == 'object' || typeof val == 'function'){
62799             return false;
62800         }
62801         return true;
62802     },
62803
62804     setValue : function(prop, value){
62805         this.source[prop] = value;
62806         this.store.getById(prop).set('value', value);
62807     },
62808
62809     getSource : function(){
62810         return this.source;
62811     }
62812 });
62813
62814 Roo.grid.PropertyColumnModel = function(grid, store){
62815     this.grid = grid;
62816     var g = Roo.grid;
62817     g.PropertyColumnModel.superclass.constructor.call(this, [
62818         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62819         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62820     ]);
62821     this.store = store;
62822     this.bselect = Roo.DomHelper.append(document.body, {
62823         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62824             {tag: 'option', value: 'true', html: 'true'},
62825             {tag: 'option', value: 'false', html: 'false'}
62826         ]
62827     });
62828     Roo.id(this.bselect);
62829     var f = Roo.form;
62830     this.editors = {
62831         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62832         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62833         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62834         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62835         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62836     };
62837     this.renderCellDelegate = this.renderCell.createDelegate(this);
62838     this.renderPropDelegate = this.renderProp.createDelegate(this);
62839 };
62840
62841 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62842     
62843     
62844     nameText : 'Name',
62845     valueText : 'Value',
62846     
62847     dateFormat : 'm/j/Y',
62848     
62849     
62850     renderDate : function(dateVal){
62851         return dateVal.dateFormat(this.dateFormat);
62852     },
62853
62854     renderBool : function(bVal){
62855         return bVal ? 'true' : 'false';
62856     },
62857
62858     isCellEditable : function(colIndex, rowIndex){
62859         return colIndex == 1;
62860     },
62861
62862     getRenderer : function(col){
62863         return col == 1 ?
62864             this.renderCellDelegate : this.renderPropDelegate;
62865     },
62866
62867     renderProp : function(v){
62868         return this.getPropertyName(v);
62869     },
62870
62871     renderCell : function(val){
62872         var rv = val;
62873         if(val instanceof Date){
62874             rv = this.renderDate(val);
62875         }else if(typeof val == 'boolean'){
62876             rv = this.renderBool(val);
62877         }
62878         return Roo.util.Format.htmlEncode(rv);
62879     },
62880
62881     getPropertyName : function(name){
62882         var pn = this.grid.propertyNames;
62883         return pn && pn[name] ? pn[name] : name;
62884     },
62885
62886     getCellEditor : function(colIndex, rowIndex){
62887         var p = this.store.getProperty(rowIndex);
62888         var n = p.data['name'], val = p.data['value'];
62889         
62890         if(typeof(this.grid.customEditors[n]) == 'string'){
62891             return this.editors[this.grid.customEditors[n]];
62892         }
62893         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62894             return this.grid.customEditors[n];
62895         }
62896         if(val instanceof Date){
62897             return this.editors['date'];
62898         }else if(typeof val == 'number'){
62899             return this.editors['number'];
62900         }else if(typeof val == 'boolean'){
62901             return this.editors['boolean'];
62902         }else{
62903             return this.editors['string'];
62904         }
62905     }
62906 });
62907
62908 /**
62909  * @class Roo.grid.PropertyGrid
62910  * @extends Roo.grid.EditorGrid
62911  * This class represents the  interface of a component based property grid control.
62912  * <br><br>Usage:<pre><code>
62913  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62914       
62915  });
62916  // set any options
62917  grid.render();
62918  * </code></pre>
62919   
62920  * @constructor
62921  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62922  * The container MUST have some type of size defined for the grid to fill. The container will be
62923  * automatically set to position relative if it isn't already.
62924  * @param {Object} config A config object that sets properties on this grid.
62925  */
62926 Roo.grid.PropertyGrid = function(container, config){
62927     config = config || {};
62928     var store = new Roo.grid.PropertyStore(this);
62929     this.store = store;
62930     var cm = new Roo.grid.PropertyColumnModel(this, store);
62931     store.store.sort('name', 'ASC');
62932     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62933         ds: store.store,
62934         cm: cm,
62935         enableColLock:false,
62936         enableColumnMove:false,
62937         stripeRows:false,
62938         trackMouseOver: false,
62939         clicksToEdit:1
62940     }, config));
62941     this.getGridEl().addClass('x-props-grid');
62942     this.lastEditRow = null;
62943     this.on('columnresize', this.onColumnResize, this);
62944     this.addEvents({
62945          /**
62946              * @event beforepropertychange
62947              * Fires before a property changes (return false to stop?)
62948              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62949              * @param {String} id Record Id
62950              * @param {String} newval New Value
62951          * @param {String} oldval Old Value
62952              */
62953         "beforepropertychange": true,
62954         /**
62955              * @event propertychange
62956              * Fires after a property changes
62957              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62958              * @param {String} id Record Id
62959              * @param {String} newval New Value
62960          * @param {String} oldval Old Value
62961              */
62962         "propertychange": true
62963     });
62964     this.customEditors = this.customEditors || {};
62965 };
62966 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62967     
62968      /**
62969      * @cfg {Object} customEditors map of colnames=> custom editors.
62970      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62971      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62972      * false disables editing of the field.
62973          */
62974     
62975       /**
62976      * @cfg {Object} propertyNames map of property Names to their displayed value
62977          */
62978     
62979     render : function(){
62980         Roo.grid.PropertyGrid.superclass.render.call(this);
62981         this.autoSize.defer(100, this);
62982     },
62983
62984     autoSize : function(){
62985         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62986         if(this.view){
62987             this.view.fitColumns();
62988         }
62989     },
62990
62991     onColumnResize : function(){
62992         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62993         this.autoSize();
62994     },
62995     /**
62996      * Sets the data for the Grid
62997      * accepts a Key => Value object of all the elements avaiable.
62998      * @param {Object} data  to appear in grid.
62999      */
63000     setSource : function(source){
63001         this.store.setSource(source);
63002         //this.autoSize();
63003     },
63004     /**
63005      * Gets all the data from the grid.
63006      * @return {Object} data  data stored in grid
63007      */
63008     getSource : function(){
63009         return this.store.getSource();
63010     }
63011 });/*
63012   
63013  * Licence LGPL
63014  
63015  */
63016  
63017 /**
63018  * @class Roo.grid.Calendar
63019  * @extends Roo.grid.Grid
63020  * This class extends the Grid to provide a calendar widget
63021  * <br><br>Usage:<pre><code>
63022  var grid = new Roo.grid.Calendar("my-container-id", {
63023      ds: myDataStore,
63024      cm: myColModel,
63025      selModel: mySelectionModel,
63026      autoSizeColumns: true,
63027      monitorWindowResize: false,
63028      trackMouseOver: true
63029      eventstore : real data store..
63030  });
63031  // set any options
63032  grid.render();
63033   
63034   * @constructor
63035  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
63036  * The container MUST have some type of size defined for the grid to fill. The container will be
63037  * automatically set to position relative if it isn't already.
63038  * @param {Object} config A config object that sets properties on this grid.
63039  */
63040 Roo.grid.Calendar = function(container, config){
63041         // initialize the container
63042         this.container = Roo.get(container);
63043         this.container.update("");
63044         this.container.setStyle("overflow", "hidden");
63045     this.container.addClass('x-grid-container');
63046
63047     this.id = this.container.id;
63048
63049     Roo.apply(this, config);
63050     // check and correct shorthanded configs
63051     
63052     var rows = [];
63053     var d =1;
63054     for (var r = 0;r < 6;r++) {
63055         
63056         rows[r]=[];
63057         for (var c =0;c < 7;c++) {
63058             rows[r][c]= '';
63059         }
63060     }
63061     if (this.eventStore) {
63062         this.eventStore= Roo.factory(this.eventStore, Roo.data);
63063         this.eventStore.on('load',this.onLoad, this);
63064         this.eventStore.on('beforeload',this.clearEvents, this);
63065          
63066     }
63067     
63068     this.dataSource = new Roo.data.Store({
63069             proxy: new Roo.data.MemoryProxy(rows),
63070             reader: new Roo.data.ArrayReader({}, [
63071                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
63072     });
63073
63074     this.dataSource.load();
63075     this.ds = this.dataSource;
63076     this.ds.xmodule = this.xmodule || false;
63077     
63078     
63079     var cellRender = function(v,x,r)
63080     {
63081         return String.format(
63082             '<div class="fc-day  fc-widget-content"><div>' +
63083                 '<div class="fc-event-container"></div>' +
63084                 '<div class="fc-day-number">{0}</div>'+
63085                 
63086                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
63087             '</div></div>', v);
63088     
63089     }
63090     
63091     
63092     this.colModel = new Roo.grid.ColumnModel( [
63093         {
63094             xtype: 'ColumnModel',
63095             xns: Roo.grid,
63096             dataIndex : 'weekday0',
63097             header : 'Sunday',
63098             renderer : cellRender
63099         },
63100         {
63101             xtype: 'ColumnModel',
63102             xns: Roo.grid,
63103             dataIndex : 'weekday1',
63104             header : 'Monday',
63105             renderer : cellRender
63106         },
63107         {
63108             xtype: 'ColumnModel',
63109             xns: Roo.grid,
63110             dataIndex : 'weekday2',
63111             header : 'Tuesday',
63112             renderer : cellRender
63113         },
63114         {
63115             xtype: 'ColumnModel',
63116             xns: Roo.grid,
63117             dataIndex : 'weekday3',
63118             header : 'Wednesday',
63119             renderer : cellRender
63120         },
63121         {
63122             xtype: 'ColumnModel',
63123             xns: Roo.grid,
63124             dataIndex : 'weekday4',
63125             header : 'Thursday',
63126             renderer : cellRender
63127         },
63128         {
63129             xtype: 'ColumnModel',
63130             xns: Roo.grid,
63131             dataIndex : 'weekday5',
63132             header : 'Friday',
63133             renderer : cellRender
63134         },
63135         {
63136             xtype: 'ColumnModel',
63137             xns: Roo.grid,
63138             dataIndex : 'weekday6',
63139             header : 'Saturday',
63140             renderer : cellRender
63141         }
63142     ]);
63143     this.cm = this.colModel;
63144     this.cm.xmodule = this.xmodule || false;
63145  
63146         
63147           
63148     //this.selModel = new Roo.grid.CellSelectionModel();
63149     //this.sm = this.selModel;
63150     //this.selModel.init(this);
63151     
63152     
63153     if(this.width){
63154         this.container.setWidth(this.width);
63155     }
63156
63157     if(this.height){
63158         this.container.setHeight(this.height);
63159     }
63160     /** @private */
63161         this.addEvents({
63162         // raw events
63163         /**
63164          * @event click
63165          * The raw click event for the entire grid.
63166          * @param {Roo.EventObject} e
63167          */
63168         "click" : true,
63169         /**
63170          * @event dblclick
63171          * The raw dblclick event for the entire grid.
63172          * @param {Roo.EventObject} e
63173          */
63174         "dblclick" : true,
63175         /**
63176          * @event contextmenu
63177          * The raw contextmenu event for the entire grid.
63178          * @param {Roo.EventObject} e
63179          */
63180         "contextmenu" : true,
63181         /**
63182          * @event mousedown
63183          * The raw mousedown event for the entire grid.
63184          * @param {Roo.EventObject} e
63185          */
63186         "mousedown" : true,
63187         /**
63188          * @event mouseup
63189          * The raw mouseup event for the entire grid.
63190          * @param {Roo.EventObject} e
63191          */
63192         "mouseup" : true,
63193         /**
63194          * @event mouseover
63195          * The raw mouseover event for the entire grid.
63196          * @param {Roo.EventObject} e
63197          */
63198         "mouseover" : true,
63199         /**
63200          * @event mouseout
63201          * The raw mouseout event for the entire grid.
63202          * @param {Roo.EventObject} e
63203          */
63204         "mouseout" : true,
63205         /**
63206          * @event keypress
63207          * The raw keypress event for the entire grid.
63208          * @param {Roo.EventObject} e
63209          */
63210         "keypress" : true,
63211         /**
63212          * @event keydown
63213          * The raw keydown event for the entire grid.
63214          * @param {Roo.EventObject} e
63215          */
63216         "keydown" : true,
63217
63218         // custom events
63219
63220         /**
63221          * @event cellclick
63222          * Fires when a cell is clicked
63223          * @param {Grid} this
63224          * @param {Number} rowIndex
63225          * @param {Number} columnIndex
63226          * @param {Roo.EventObject} e
63227          */
63228         "cellclick" : true,
63229         /**
63230          * @event celldblclick
63231          * Fires when a cell is double clicked
63232          * @param {Grid} this
63233          * @param {Number} rowIndex
63234          * @param {Number} columnIndex
63235          * @param {Roo.EventObject} e
63236          */
63237         "celldblclick" : true,
63238         /**
63239          * @event rowclick
63240          * Fires when a row is clicked
63241          * @param {Grid} this
63242          * @param {Number} rowIndex
63243          * @param {Roo.EventObject} e
63244          */
63245         "rowclick" : true,
63246         /**
63247          * @event rowdblclick
63248          * Fires when a row is double clicked
63249          * @param {Grid} this
63250          * @param {Number} rowIndex
63251          * @param {Roo.EventObject} e
63252          */
63253         "rowdblclick" : true,
63254         /**
63255          * @event headerclick
63256          * Fires when a header is clicked
63257          * @param {Grid} this
63258          * @param {Number} columnIndex
63259          * @param {Roo.EventObject} e
63260          */
63261         "headerclick" : true,
63262         /**
63263          * @event headerdblclick
63264          * Fires when a header cell is double clicked
63265          * @param {Grid} this
63266          * @param {Number} columnIndex
63267          * @param {Roo.EventObject} e
63268          */
63269         "headerdblclick" : true,
63270         /**
63271          * @event rowcontextmenu
63272          * Fires when a row is right clicked
63273          * @param {Grid} this
63274          * @param {Number} rowIndex
63275          * @param {Roo.EventObject} e
63276          */
63277         "rowcontextmenu" : true,
63278         /**
63279          * @event cellcontextmenu
63280          * Fires when a cell is right clicked
63281          * @param {Grid} this
63282          * @param {Number} rowIndex
63283          * @param {Number} cellIndex
63284          * @param {Roo.EventObject} e
63285          */
63286          "cellcontextmenu" : true,
63287         /**
63288          * @event headercontextmenu
63289          * Fires when a header is right clicked
63290          * @param {Grid} this
63291          * @param {Number} columnIndex
63292          * @param {Roo.EventObject} e
63293          */
63294         "headercontextmenu" : true,
63295         /**
63296          * @event bodyscroll
63297          * Fires when the body element is scrolled
63298          * @param {Number} scrollLeft
63299          * @param {Number} scrollTop
63300          */
63301         "bodyscroll" : true,
63302         /**
63303          * @event columnresize
63304          * Fires when the user resizes a column
63305          * @param {Number} columnIndex
63306          * @param {Number} newSize
63307          */
63308         "columnresize" : true,
63309         /**
63310          * @event columnmove
63311          * Fires when the user moves a column
63312          * @param {Number} oldIndex
63313          * @param {Number} newIndex
63314          */
63315         "columnmove" : true,
63316         /**
63317          * @event startdrag
63318          * Fires when row(s) start being dragged
63319          * @param {Grid} this
63320          * @param {Roo.GridDD} dd The drag drop object
63321          * @param {event} e The raw browser event
63322          */
63323         "startdrag" : true,
63324         /**
63325          * @event enddrag
63326          * Fires when a drag operation is complete
63327          * @param {Grid} this
63328          * @param {Roo.GridDD} dd The drag drop object
63329          * @param {event} e The raw browser event
63330          */
63331         "enddrag" : true,
63332         /**
63333          * @event dragdrop
63334          * Fires when dragged row(s) are dropped on a valid DD target
63335          * @param {Grid} this
63336          * @param {Roo.GridDD} dd The drag drop object
63337          * @param {String} targetId The target drag drop object
63338          * @param {event} e The raw browser event
63339          */
63340         "dragdrop" : true,
63341         /**
63342          * @event dragover
63343          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
63344          * @param {Grid} this
63345          * @param {Roo.GridDD} dd The drag drop object
63346          * @param {String} targetId The target drag drop object
63347          * @param {event} e The raw browser event
63348          */
63349         "dragover" : true,
63350         /**
63351          * @event dragenter
63352          *  Fires when the dragged row(s) first cross another DD target while being dragged
63353          * @param {Grid} this
63354          * @param {Roo.GridDD} dd The drag drop object
63355          * @param {String} targetId The target drag drop object
63356          * @param {event} e The raw browser event
63357          */
63358         "dragenter" : true,
63359         /**
63360          * @event dragout
63361          * Fires when the dragged row(s) leave another DD target while being dragged
63362          * @param {Grid} this
63363          * @param {Roo.GridDD} dd The drag drop object
63364          * @param {String} targetId The target drag drop object
63365          * @param {event} e The raw browser event
63366          */
63367         "dragout" : true,
63368         /**
63369          * @event rowclass
63370          * Fires when a row is rendered, so you can change add a style to it.
63371          * @param {GridView} gridview   The grid view
63372          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
63373          */
63374         'rowclass' : true,
63375
63376         /**
63377          * @event render
63378          * Fires when the grid is rendered
63379          * @param {Grid} grid
63380          */
63381         'render' : true,
63382             /**
63383              * @event select
63384              * Fires when a date is selected
63385              * @param {DatePicker} this
63386              * @param {Date} date The selected date
63387              */
63388         'select': true,
63389         /**
63390              * @event monthchange
63391              * Fires when the displayed month changes 
63392              * @param {DatePicker} this
63393              * @param {Date} date The selected month
63394              */
63395         'monthchange': true,
63396         /**
63397              * @event evententer
63398              * Fires when mouse over an event
63399              * @param {Calendar} this
63400              * @param {event} Event
63401              */
63402         'evententer': true,
63403         /**
63404              * @event eventleave
63405              * Fires when the mouse leaves an
63406              * @param {Calendar} this
63407              * @param {event}
63408              */
63409         'eventleave': true,
63410         /**
63411              * @event eventclick
63412              * Fires when the mouse click an
63413              * @param {Calendar} this
63414              * @param {event}
63415              */
63416         'eventclick': true,
63417         /**
63418              * @event eventrender
63419              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63420              * @param {Calendar} this
63421              * @param {data} data to be modified
63422              */
63423         'eventrender': true
63424         
63425     });
63426
63427     Roo.grid.Grid.superclass.constructor.call(this);
63428     this.on('render', function() {
63429         this.view.el.addClass('x-grid-cal'); 
63430         
63431         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63432
63433     },this);
63434     
63435     if (!Roo.grid.Calendar.style) {
63436         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63437             
63438             
63439             '.x-grid-cal .x-grid-col' :  {
63440                 height: 'auto !important',
63441                 'vertical-align': 'top'
63442             },
63443             '.x-grid-cal  .fc-event-hori' : {
63444                 height: '14px'
63445             }
63446              
63447             
63448         }, Roo.id());
63449     }
63450
63451     
63452     
63453 };
63454 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63455     /**
63456      * @cfg {Store} eventStore The store that loads events.
63457      */
63458     eventStore : 25,
63459
63460      
63461     activeDate : false,
63462     startDay : 0,
63463     autoWidth : true,
63464     monitorWindowResize : false,
63465
63466     
63467     resizeColumns : function() {
63468         var col = (this.view.el.getWidth() / 7) - 3;
63469         // loop through cols, and setWidth
63470         for(var i =0 ; i < 7 ; i++){
63471             this.cm.setColumnWidth(i, col);
63472         }
63473     },
63474      setDate :function(date) {
63475         
63476         Roo.log('setDate?');
63477         
63478         this.resizeColumns();
63479         var vd = this.activeDate;
63480         this.activeDate = date;
63481 //        if(vd && this.el){
63482 //            var t = date.getTime();
63483 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63484 //                Roo.log('using add remove');
63485 //                
63486 //                this.fireEvent('monthchange', this, date);
63487 //                
63488 //                this.cells.removeClass("fc-state-highlight");
63489 //                this.cells.each(function(c){
63490 //                   if(c.dateValue == t){
63491 //                       c.addClass("fc-state-highlight");
63492 //                       setTimeout(function(){
63493 //                            try{c.dom.firstChild.focus();}catch(e){}
63494 //                       }, 50);
63495 //                       return false;
63496 //                   }
63497 //                   return true;
63498 //                });
63499 //                return;
63500 //            }
63501 //        }
63502         
63503         var days = date.getDaysInMonth();
63504         
63505         var firstOfMonth = date.getFirstDateOfMonth();
63506         var startingPos = firstOfMonth.getDay()-this.startDay;
63507         
63508         if(startingPos < this.startDay){
63509             startingPos += 7;
63510         }
63511         
63512         var pm = date.add(Date.MONTH, -1);
63513         var prevStart = pm.getDaysInMonth()-startingPos;
63514 //        
63515         
63516         
63517         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63518         
63519         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63520         //this.cells.addClassOnOver('fc-state-hover');
63521         
63522         var cells = this.cells.elements;
63523         var textEls = this.textNodes;
63524         
63525         //Roo.each(cells, function(cell){
63526         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63527         //});
63528         
63529         days += startingPos;
63530
63531         // convert everything to numbers so it's fast
63532         var day = 86400000;
63533         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63534         //Roo.log(d);
63535         //Roo.log(pm);
63536         //Roo.log(prevStart);
63537         
63538         var today = new Date().clearTime().getTime();
63539         var sel = date.clearTime().getTime();
63540         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63541         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63542         var ddMatch = this.disabledDatesRE;
63543         var ddText = this.disabledDatesText;
63544         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63545         var ddaysText = this.disabledDaysText;
63546         var format = this.format;
63547         
63548         var setCellClass = function(cal, cell){
63549             
63550             //Roo.log('set Cell Class');
63551             cell.title = "";
63552             var t = d.getTime();
63553             
63554             //Roo.log(d);
63555             
63556             
63557             cell.dateValue = t;
63558             if(t == today){
63559                 cell.className += " fc-today";
63560                 cell.className += " fc-state-highlight";
63561                 cell.title = cal.todayText;
63562             }
63563             if(t == sel){
63564                 // disable highlight in other month..
63565                 cell.className += " fc-state-highlight";
63566                 
63567             }
63568             // disabling
63569             if(t < min) {
63570                 //cell.className = " fc-state-disabled";
63571                 cell.title = cal.minText;
63572                 return;
63573             }
63574             if(t > max) {
63575                 //cell.className = " fc-state-disabled";
63576                 cell.title = cal.maxText;
63577                 return;
63578             }
63579             if(ddays){
63580                 if(ddays.indexOf(d.getDay()) != -1){
63581                     // cell.title = ddaysText;
63582                    // cell.className = " fc-state-disabled";
63583                 }
63584             }
63585             if(ddMatch && format){
63586                 var fvalue = d.dateFormat(format);
63587                 if(ddMatch.test(fvalue)){
63588                     cell.title = ddText.replace("%0", fvalue);
63589                    cell.className = " fc-state-disabled";
63590                 }
63591             }
63592             
63593             if (!cell.initialClassName) {
63594                 cell.initialClassName = cell.dom.className;
63595             }
63596             
63597             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63598         };
63599
63600         var i = 0;
63601         
63602         for(; i < startingPos; i++) {
63603             cells[i].dayName =  (++prevStart);
63604             Roo.log(textEls[i]);
63605             d.setDate(d.getDate()+1);
63606             
63607             //cells[i].className = "fc-past fc-other-month";
63608             setCellClass(this, cells[i]);
63609         }
63610         
63611         var intDay = 0;
63612         
63613         for(; i < days; i++){
63614             intDay = i - startingPos + 1;
63615             cells[i].dayName =  (intDay);
63616             d.setDate(d.getDate()+1);
63617             
63618             cells[i].className = ''; // "x-date-active";
63619             setCellClass(this, cells[i]);
63620         }
63621         var extraDays = 0;
63622         
63623         for(; i < 42; i++) {
63624             //textEls[i].innerHTML = (++extraDays);
63625             
63626             d.setDate(d.getDate()+1);
63627             cells[i].dayName = (++extraDays);
63628             cells[i].className = "fc-future fc-other-month";
63629             setCellClass(this, cells[i]);
63630         }
63631         
63632         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63633         
63634         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63635         
63636         // this will cause all the cells to mis
63637         var rows= [];
63638         var i =0;
63639         for (var r = 0;r < 6;r++) {
63640             for (var c =0;c < 7;c++) {
63641                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63642             }    
63643         }
63644         
63645         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63646         for(i=0;i<cells.length;i++) {
63647             
63648             this.cells.elements[i].dayName = cells[i].dayName ;
63649             this.cells.elements[i].className = cells[i].className;
63650             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63651             this.cells.elements[i].title = cells[i].title ;
63652             this.cells.elements[i].dateValue = cells[i].dateValue ;
63653         }
63654         
63655         
63656         
63657         
63658         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63659         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63660         
63661         ////if(totalRows != 6){
63662             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63663            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63664        // }
63665         
63666         this.fireEvent('monthchange', this, date);
63667         
63668         
63669     },
63670  /**
63671      * Returns the grid's SelectionModel.
63672      * @return {SelectionModel}
63673      */
63674     getSelectionModel : function(){
63675         if(!this.selModel){
63676             this.selModel = new Roo.grid.CellSelectionModel();
63677         }
63678         return this.selModel;
63679     },
63680
63681     load: function() {
63682         this.eventStore.load()
63683         
63684         
63685         
63686     },
63687     
63688     findCell : function(dt) {
63689         dt = dt.clearTime().getTime();
63690         var ret = false;
63691         this.cells.each(function(c){
63692             //Roo.log("check " +c.dateValue + '?=' + dt);
63693             if(c.dateValue == dt){
63694                 ret = c;
63695                 return false;
63696             }
63697             return true;
63698         });
63699         
63700         return ret;
63701     },
63702     
63703     findCells : function(rec) {
63704         var s = rec.data.start_dt.clone().clearTime().getTime();
63705        // Roo.log(s);
63706         var e= rec.data.end_dt.clone().clearTime().getTime();
63707        // Roo.log(e);
63708         var ret = [];
63709         this.cells.each(function(c){
63710              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63711             
63712             if(c.dateValue > e){
63713                 return ;
63714             }
63715             if(c.dateValue < s){
63716                 return ;
63717             }
63718             ret.push(c);
63719         });
63720         
63721         return ret;    
63722     },
63723     
63724     findBestRow: function(cells)
63725     {
63726         var ret = 0;
63727         
63728         for (var i =0 ; i < cells.length;i++) {
63729             ret  = Math.max(cells[i].rows || 0,ret);
63730         }
63731         return ret;
63732         
63733     },
63734     
63735     
63736     addItem : function(rec)
63737     {
63738         // look for vertical location slot in
63739         var cells = this.findCells(rec);
63740         
63741         rec.row = this.findBestRow(cells);
63742         
63743         // work out the location.
63744         
63745         var crow = false;
63746         var rows = [];
63747         for(var i =0; i < cells.length; i++) {
63748             if (!crow) {
63749                 crow = {
63750                     start : cells[i],
63751                     end :  cells[i]
63752                 };
63753                 continue;
63754             }
63755             if (crow.start.getY() == cells[i].getY()) {
63756                 // on same row.
63757                 crow.end = cells[i];
63758                 continue;
63759             }
63760             // different row.
63761             rows.push(crow);
63762             crow = {
63763                 start: cells[i],
63764                 end : cells[i]
63765             };
63766             
63767         }
63768         
63769         rows.push(crow);
63770         rec.els = [];
63771         rec.rows = rows;
63772         rec.cells = cells;
63773         for (var i = 0; i < cells.length;i++) {
63774             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63775             
63776         }
63777         
63778         
63779     },
63780     
63781     clearEvents: function() {
63782         
63783         if (!this.eventStore.getCount()) {
63784             return;
63785         }
63786         // reset number of rows in cells.
63787         Roo.each(this.cells.elements, function(c){
63788             c.rows = 0;
63789         });
63790         
63791         this.eventStore.each(function(e) {
63792             this.clearEvent(e);
63793         },this);
63794         
63795     },
63796     
63797     clearEvent : function(ev)
63798     {
63799         if (ev.els) {
63800             Roo.each(ev.els, function(el) {
63801                 el.un('mouseenter' ,this.onEventEnter, this);
63802                 el.un('mouseleave' ,this.onEventLeave, this);
63803                 el.remove();
63804             },this);
63805             ev.els = [];
63806         }
63807     },
63808     
63809     
63810     renderEvent : function(ev,ctr) {
63811         if (!ctr) {
63812              ctr = this.view.el.select('.fc-event-container',true).first();
63813         }
63814         
63815          
63816         this.clearEvent(ev);
63817             //code
63818        
63819         
63820         
63821         ev.els = [];
63822         var cells = ev.cells;
63823         var rows = ev.rows;
63824         this.fireEvent('eventrender', this, ev);
63825         
63826         for(var i =0; i < rows.length; i++) {
63827             
63828             cls = '';
63829             if (i == 0) {
63830                 cls += ' fc-event-start';
63831             }
63832             if ((i+1) == rows.length) {
63833                 cls += ' fc-event-end';
63834             }
63835             
63836             //Roo.log(ev.data);
63837             // how many rows should it span..
63838             var cg = this.eventTmpl.append(ctr,Roo.apply({
63839                 fccls : cls
63840                 
63841             }, ev.data) , true);
63842             
63843             
63844             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63845             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63846             cg.on('click', this.onEventClick, this, ev);
63847             
63848             ev.els.push(cg);
63849             
63850             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63851             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63852             //Roo.log(cg);
63853              
63854             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63855             cg.setWidth(ebox.right - sbox.x -2);
63856         }
63857     },
63858     
63859     renderEvents: function()
63860     {   
63861         // first make sure there is enough space..
63862         
63863         if (!this.eventTmpl) {
63864             this.eventTmpl = new Roo.Template(
63865                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63866                     '<div class="fc-event-inner">' +
63867                         '<span class="fc-event-time">{time}</span>' +
63868                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63869                     '</div>' +
63870                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63871                 '</div>'
63872             );
63873                 
63874         }
63875                
63876         
63877         
63878         this.cells.each(function(c) {
63879             //Roo.log(c.select('.fc-day-content div',true).first());
63880             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63881         });
63882         
63883         var ctr = this.view.el.select('.fc-event-container',true).first();
63884         
63885         var cls;
63886         this.eventStore.each(function(ev){
63887             
63888             this.renderEvent(ev);
63889              
63890              
63891         }, this);
63892         this.view.layout();
63893         
63894     },
63895     
63896     onEventEnter: function (e, el,event,d) {
63897         this.fireEvent('evententer', this, el, event);
63898     },
63899     
63900     onEventLeave: function (e, el,event,d) {
63901         this.fireEvent('eventleave', this, el, event);
63902     },
63903     
63904     onEventClick: function (e, el,event,d) {
63905         this.fireEvent('eventclick', this, el, event);
63906     },
63907     
63908     onMonthChange: function () {
63909         this.store.load();
63910     },
63911     
63912     onLoad: function () {
63913         
63914         //Roo.log('calendar onload');
63915 //         
63916         if(this.eventStore.getCount() > 0){
63917             
63918            
63919             
63920             this.eventStore.each(function(d){
63921                 
63922                 
63923                 // FIXME..
63924                 var add =   d.data;
63925                 if (typeof(add.end_dt) == 'undefined')  {
63926                     Roo.log("Missing End time in calendar data: ");
63927                     Roo.log(d);
63928                     return;
63929                 }
63930                 if (typeof(add.start_dt) == 'undefined')  {
63931                     Roo.log("Missing Start time in calendar data: ");
63932                     Roo.log(d);
63933                     return;
63934                 }
63935                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63936                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63937                 add.id = add.id || d.id;
63938                 add.title = add.title || '??';
63939                 
63940                 this.addItem(d);
63941                 
63942              
63943             },this);
63944         }
63945         
63946         this.renderEvents();
63947     }
63948     
63949
63950 });
63951 /*
63952  grid : {
63953                 xtype: 'Grid',
63954                 xns: Roo.grid,
63955                 listeners : {
63956                     render : function ()
63957                     {
63958                         _this.grid = this;
63959                         
63960                         if (!this.view.el.hasClass('course-timesheet')) {
63961                             this.view.el.addClass('course-timesheet');
63962                         }
63963                         if (this.tsStyle) {
63964                             this.ds.load({});
63965                             return; 
63966                         }
63967                         Roo.log('width');
63968                         Roo.log(_this.grid.view.el.getWidth());
63969                         
63970                         
63971                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63972                             '.course-timesheet .x-grid-row' : {
63973                                 height: '80px'
63974                             },
63975                             '.x-grid-row td' : {
63976                                 'vertical-align' : 0
63977                             },
63978                             '.course-edit-link' : {
63979                                 'color' : 'blue',
63980                                 'text-overflow' : 'ellipsis',
63981                                 'overflow' : 'hidden',
63982                                 'white-space' : 'nowrap',
63983                                 'cursor' : 'pointer'
63984                             },
63985                             '.sub-link' : {
63986                                 'color' : 'green'
63987                             },
63988                             '.de-act-sup-link' : {
63989                                 'color' : 'purple',
63990                                 'text-decoration' : 'line-through'
63991                             },
63992                             '.de-act-link' : {
63993                                 'color' : 'red',
63994                                 'text-decoration' : 'line-through'
63995                             },
63996                             '.course-timesheet .course-highlight' : {
63997                                 'border-top-style': 'dashed !important',
63998                                 'border-bottom-bottom': 'dashed !important'
63999                             },
64000                             '.course-timesheet .course-item' : {
64001                                 'font-family'   : 'tahoma, arial, helvetica',
64002                                 'font-size'     : '11px',
64003                                 'overflow'      : 'hidden',
64004                                 'padding-left'  : '10px',
64005                                 'padding-right' : '10px',
64006                                 'padding-top' : '10px' 
64007                             }
64008                             
64009                         }, Roo.id());
64010                                 this.ds.load({});
64011                     }
64012                 },
64013                 autoWidth : true,
64014                 monitorWindowResize : false,
64015                 cellrenderer : function(v,x,r)
64016                 {
64017                     return v;
64018                 },
64019                 sm : {
64020                     xtype: 'CellSelectionModel',
64021                     xns: Roo.grid
64022                 },
64023                 dataSource : {
64024                     xtype: 'Store',
64025                     xns: Roo.data,
64026                     listeners : {
64027                         beforeload : function (_self, options)
64028                         {
64029                             options.params = options.params || {};
64030                             options.params._month = _this.monthField.getValue();
64031                             options.params.limit = 9999;
64032                             options.params['sort'] = 'when_dt';    
64033                             options.params['dir'] = 'ASC';    
64034                             this.proxy.loadResponse = this.loadResponse;
64035                             Roo.log("load?");
64036                             //this.addColumns();
64037                         },
64038                         load : function (_self, records, options)
64039                         {
64040                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
64041                                 // if you click on the translation.. you can edit it...
64042                                 var el = Roo.get(this);
64043                                 var id = el.dom.getAttribute('data-id');
64044                                 var d = el.dom.getAttribute('data-date');
64045                                 var t = el.dom.getAttribute('data-time');
64046                                 //var id = this.child('span').dom.textContent;
64047                                 
64048                                 //Roo.log(this);
64049                                 Pman.Dialog.CourseCalendar.show({
64050                                     id : id,
64051                                     when_d : d,
64052                                     when_t : t,
64053                                     productitem_active : id ? 1 : 0
64054                                 }, function() {
64055                                     _this.grid.ds.load({});
64056                                 });
64057                            
64058                            });
64059                            
64060                            _this.panel.fireEvent('resize', [ '', '' ]);
64061                         }
64062                     },
64063                     loadResponse : function(o, success, response){
64064                             // this is overridden on before load..
64065                             
64066                             Roo.log("our code?");       
64067                             //Roo.log(success);
64068                             //Roo.log(response)
64069                             delete this.activeRequest;
64070                             if(!success){
64071                                 this.fireEvent("loadexception", this, o, response);
64072                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64073                                 return;
64074                             }
64075                             var result;
64076                             try {
64077                                 result = o.reader.read(response);
64078                             }catch(e){
64079                                 Roo.log("load exception?");
64080                                 this.fireEvent("loadexception", this, o, response, e);
64081                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
64082                                 return;
64083                             }
64084                             Roo.log("ready...");        
64085                             // loop through result.records;
64086                             // and set this.tdate[date] = [] << array of records..
64087                             _this.tdata  = {};
64088                             Roo.each(result.records, function(r){
64089                                 //Roo.log(r.data);
64090                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
64091                                     _this.tdata[r.data.when_dt.format('j')] = [];
64092                                 }
64093                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
64094                             });
64095                             
64096                             //Roo.log(_this.tdata);
64097                             
64098                             result.records = [];
64099                             result.totalRecords = 6;
64100                     
64101                             // let's generate some duumy records for the rows.
64102                             //var st = _this.dateField.getValue();
64103                             
64104                             // work out monday..
64105                             //st = st.add(Date.DAY, -1 * st.format('w'));
64106                             
64107                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64108                             
64109                             var firstOfMonth = date.getFirstDayOfMonth();
64110                             var days = date.getDaysInMonth();
64111                             var d = 1;
64112                             var firstAdded = false;
64113                             for (var i = 0; i < result.totalRecords ; i++) {
64114                                 //var d= st.add(Date.DAY, i);
64115                                 var row = {};
64116                                 var added = 0;
64117                                 for(var w = 0 ; w < 7 ; w++){
64118                                     if(!firstAdded && firstOfMonth != w){
64119                                         continue;
64120                                     }
64121                                     if(d > days){
64122                                         continue;
64123                                     }
64124                                     firstAdded = true;
64125                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
64126                                     row['weekday'+w] = String.format(
64127                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
64128                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
64129                                                     d,
64130                                                     date.format('Y-m-')+dd
64131                                                 );
64132                                     added++;
64133                                     if(typeof(_this.tdata[d]) != 'undefined'){
64134                                         Roo.each(_this.tdata[d], function(r){
64135                                             var is_sub = '';
64136                                             var deactive = '';
64137                                             var id = r.id;
64138                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
64139                                             if(r.parent_id*1>0){
64140                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
64141                                                 id = r.parent_id;
64142                                             }
64143                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
64144                                                 deactive = 'de-act-link';
64145                                             }
64146                                             
64147                                             row['weekday'+w] += String.format(
64148                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
64149                                                     id, //0
64150                                                     r.product_id_name, //1
64151                                                     r.when_dt.format('h:ia'), //2
64152                                                     is_sub, //3
64153                                                     deactive, //4
64154                                                     desc // 5
64155                                             );
64156                                         });
64157                                     }
64158                                     d++;
64159                                 }
64160                                 
64161                                 // only do this if something added..
64162                                 if(added > 0){ 
64163                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
64164                                 }
64165                                 
64166                                 
64167                                 // push it twice. (second one with an hour..
64168                                 
64169                             }
64170                             //Roo.log(result);
64171                             this.fireEvent("load", this, o, o.request.arg);
64172                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
64173                         },
64174                     sortInfo : {field: 'when_dt', direction : 'ASC' },
64175                     proxy : {
64176                         xtype: 'HttpProxy',
64177                         xns: Roo.data,
64178                         method : 'GET',
64179                         url : baseURL + '/Roo/Shop_course.php'
64180                     },
64181                     reader : {
64182                         xtype: 'JsonReader',
64183                         xns: Roo.data,
64184                         id : 'id',
64185                         fields : [
64186                             {
64187                                 'name': 'id',
64188                                 'type': 'int'
64189                             },
64190                             {
64191                                 'name': 'when_dt',
64192                                 'type': 'string'
64193                             },
64194                             {
64195                                 'name': 'end_dt',
64196                                 'type': 'string'
64197                             },
64198                             {
64199                                 'name': 'parent_id',
64200                                 'type': 'int'
64201                             },
64202                             {
64203                                 'name': 'product_id',
64204                                 'type': 'int'
64205                             },
64206                             {
64207                                 'name': 'productitem_id',
64208                                 'type': 'int'
64209                             },
64210                             {
64211                                 'name': 'guid',
64212                                 'type': 'int'
64213                             }
64214                         ]
64215                     }
64216                 },
64217                 toolbar : {
64218                     xtype: 'Toolbar',
64219                     xns: Roo,
64220                     items : [
64221                         {
64222                             xtype: 'Button',
64223                             xns: Roo.Toolbar,
64224                             listeners : {
64225                                 click : function (_self, e)
64226                                 {
64227                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64228                                     sd.setMonth(sd.getMonth()-1);
64229                                     _this.monthField.setValue(sd.format('Y-m-d'));
64230                                     _this.grid.ds.load({});
64231                                 }
64232                             },
64233                             text : "Back"
64234                         },
64235                         {
64236                             xtype: 'Separator',
64237                             xns: Roo.Toolbar
64238                         },
64239                         {
64240                             xtype: 'MonthField',
64241                             xns: Roo.form,
64242                             listeners : {
64243                                 render : function (_self)
64244                                 {
64245                                     _this.monthField = _self;
64246                                    // _this.monthField.set  today
64247                                 },
64248                                 select : function (combo, date)
64249                                 {
64250                                     _this.grid.ds.load({});
64251                                 }
64252                             },
64253                             value : (function() { return new Date(); })()
64254                         },
64255                         {
64256                             xtype: 'Separator',
64257                             xns: Roo.Toolbar
64258                         },
64259                         {
64260                             xtype: 'TextItem',
64261                             xns: Roo.Toolbar,
64262                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
64263                         },
64264                         {
64265                             xtype: 'Fill',
64266                             xns: Roo.Toolbar
64267                         },
64268                         {
64269                             xtype: 'Button',
64270                             xns: Roo.Toolbar,
64271                             listeners : {
64272                                 click : function (_self, e)
64273                                 {
64274                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
64275                                     sd.setMonth(sd.getMonth()+1);
64276                                     _this.monthField.setValue(sd.format('Y-m-d'));
64277                                     _this.grid.ds.load({});
64278                                 }
64279                             },
64280                             text : "Next"
64281                         }
64282                     ]
64283                 },
64284                  
64285             }
64286         };
64287         
64288         *//*
64289  * Based on:
64290  * Ext JS Library 1.1.1
64291  * Copyright(c) 2006-2007, Ext JS, LLC.
64292  *
64293  * Originally Released Under LGPL - original licence link has changed is not relivant.
64294  *
64295  * Fork - LGPL
64296  * <script type="text/javascript">
64297  */
64298  
64299 /**
64300  * @class Roo.LoadMask
64301  * A simple utility class for generically masking elements while loading data.  If the element being masked has
64302  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
64303  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
64304  * element's UpdateManager load indicator and will be destroyed after the initial load.
64305  * @constructor
64306  * Create a new LoadMask
64307  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
64308  * @param {Object} config The config object
64309  */
64310 Roo.LoadMask = function(el, config){
64311     this.el = Roo.get(el);
64312     Roo.apply(this, config);
64313     if(this.store){
64314         this.store.on('beforeload', this.onBeforeLoad, this);
64315         this.store.on('load', this.onLoad, this);
64316         this.store.on('loadexception', this.onLoadException, this);
64317         this.removeMask = false;
64318     }else{
64319         var um = this.el.getUpdateManager();
64320         um.showLoadIndicator = false; // disable the default indicator
64321         um.on('beforeupdate', this.onBeforeLoad, this);
64322         um.on('update', this.onLoad, this);
64323         um.on('failure', this.onLoad, this);
64324         this.removeMask = true;
64325     }
64326 };
64327
64328 Roo.LoadMask.prototype = {
64329     /**
64330      * @cfg {Boolean} removeMask
64331      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
64332      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
64333      */
64334     removeMask : false,
64335     /**
64336      * @cfg {String} msg
64337      * The text to display in a centered loading message box (defaults to 'Loading...')
64338      */
64339     msg : 'Loading...',
64340     /**
64341      * @cfg {String} msgCls
64342      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
64343      */
64344     msgCls : 'x-mask-loading',
64345
64346     /**
64347      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
64348      * @type Boolean
64349      */
64350     disabled: false,
64351
64352     /**
64353      * Disables the mask to prevent it from being displayed
64354      */
64355     disable : function(){
64356        this.disabled = true;
64357     },
64358
64359     /**
64360      * Enables the mask so that it can be displayed
64361      */
64362     enable : function(){
64363         this.disabled = false;
64364     },
64365     
64366     onLoadException : function()
64367     {
64368         Roo.log(arguments);
64369         
64370         if (typeof(arguments[3]) != 'undefined') {
64371             Roo.MessageBox.alert("Error loading",arguments[3]);
64372         } 
64373         /*
64374         try {
64375             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
64376                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
64377             }   
64378         } catch(e) {
64379             
64380         }
64381         */
64382     
64383         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64384     },
64385     // private
64386     onLoad : function()
64387     {
64388         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
64389     },
64390
64391     // private
64392     onBeforeLoad : function(){
64393         if(!this.disabled){
64394             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
64395         }
64396     },
64397
64398     // private
64399     destroy : function(){
64400         if(this.store){
64401             this.store.un('beforeload', this.onBeforeLoad, this);
64402             this.store.un('load', this.onLoad, this);
64403             this.store.un('loadexception', this.onLoadException, this);
64404         }else{
64405             var um = this.el.getUpdateManager();
64406             um.un('beforeupdate', this.onBeforeLoad, this);
64407             um.un('update', this.onLoad, this);
64408             um.un('failure', this.onLoad, this);
64409         }
64410     }
64411 };/*
64412  * Based on:
64413  * Ext JS Library 1.1.1
64414  * Copyright(c) 2006-2007, Ext JS, LLC.
64415  *
64416  * Originally Released Under LGPL - original licence link has changed is not relivant.
64417  *
64418  * Fork - LGPL
64419  * <script type="text/javascript">
64420  */
64421
64422
64423 /**
64424  * @class Roo.XTemplate
64425  * @extends Roo.Template
64426  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64427 <pre><code>
64428 var t = new Roo.XTemplate(
64429         '&lt;select name="{name}"&gt;',
64430                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64431         '&lt;/select&gt;'
64432 );
64433  
64434 // then append, applying the master template values
64435  </code></pre>
64436  *
64437  * Supported features:
64438  *
64439  *  Tags:
64440
64441 <pre><code>
64442       {a_variable} - output encoded.
64443       {a_variable.format:("Y-m-d")} - call a method on the variable
64444       {a_variable:raw} - unencoded output
64445       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64446       {a_variable:this.method_on_template(...)} - call a method on the template object.
64447  
64448 </code></pre>
64449  *  The tpl tag:
64450 <pre><code>
64451         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64452         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64453         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64454         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64455   
64456         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64457         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64458 </code></pre>
64459  *      
64460  */
64461 Roo.XTemplate = function()
64462 {
64463     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64464     if (this.html) {
64465         this.compile();
64466     }
64467 };
64468
64469
64470 Roo.extend(Roo.XTemplate, Roo.Template, {
64471
64472     /**
64473      * The various sub templates
64474      */
64475     tpls : false,
64476     /**
64477      *
64478      * basic tag replacing syntax
64479      * WORD:WORD()
64480      *
64481      * // you can fake an object call by doing this
64482      *  x.t:(test,tesT) 
64483      * 
64484      */
64485     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64486
64487     /**
64488      * compile the template
64489      *
64490      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64491      *
64492      */
64493     compile: function()
64494     {
64495         var s = this.html;
64496      
64497         s = ['<tpl>', s, '</tpl>'].join('');
64498     
64499         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64500             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64501             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64502             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64503             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64504             m,
64505             id     = 0,
64506             tpls   = [];
64507     
64508         while(true == !!(m = s.match(re))){
64509             var forMatch   = m[0].match(nameRe),
64510                 ifMatch   = m[0].match(ifRe),
64511                 execMatch   = m[0].match(execRe),
64512                 namedMatch   = m[0].match(namedRe),
64513                 
64514                 exp  = null, 
64515                 fn   = null,
64516                 exec = null,
64517                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64518                 
64519             if (ifMatch) {
64520                 // if - puts fn into test..
64521                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64522                 if(exp){
64523                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64524                 }
64525             }
64526             
64527             if (execMatch) {
64528                 // exec - calls a function... returns empty if true is  returned.
64529                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64530                 if(exp){
64531                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64532                 }
64533             }
64534             
64535             
64536             if (name) {
64537                 // for = 
64538                 switch(name){
64539                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64540                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64541                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64542                 }
64543             }
64544             var uid = namedMatch ? namedMatch[1] : id;
64545             
64546             
64547             tpls.push({
64548                 id:     namedMatch ? namedMatch[1] : id,
64549                 target: name,
64550                 exec:   exec,
64551                 test:   fn,
64552                 body:   m[1] || ''
64553             });
64554             if (namedMatch) {
64555                 s = s.replace(m[0], '');
64556             } else { 
64557                 s = s.replace(m[0], '{xtpl'+ id + '}');
64558             }
64559             ++id;
64560         }
64561         this.tpls = [];
64562         for(var i = tpls.length-1; i >= 0; --i){
64563             this.compileTpl(tpls[i]);
64564             this.tpls[tpls[i].id] = tpls[i];
64565         }
64566         this.master = tpls[tpls.length-1];
64567         return this;
64568     },
64569     /**
64570      * same as applyTemplate, except it's done to one of the subTemplates
64571      * when using named templates, you can do:
64572      *
64573      * var str = pl.applySubTemplate('your-name', values);
64574      *
64575      * 
64576      * @param {Number} id of the template
64577      * @param {Object} values to apply to template
64578      * @param {Object} parent (normaly the instance of this object)
64579      */
64580     applySubTemplate : function(id, values, parent)
64581     {
64582         
64583         
64584         var t = this.tpls[id];
64585         
64586         
64587         try { 
64588             if(t.test && !t.test.call(this, values, parent)){
64589                 return '';
64590             }
64591         } catch(e) {
64592             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64593             Roo.log(e.toString());
64594             Roo.log(t.test);
64595             return ''
64596         }
64597         try { 
64598             
64599             if(t.exec && t.exec.call(this, values, parent)){
64600                 return '';
64601             }
64602         } catch(e) {
64603             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64604             Roo.log(e.toString());
64605             Roo.log(t.exec);
64606             return ''
64607         }
64608         try {
64609             var vs = t.target ? t.target.call(this, values, parent) : values;
64610             parent = t.target ? values : parent;
64611             if(t.target && vs instanceof Array){
64612                 var buf = [];
64613                 for(var i = 0, len = vs.length; i < len; i++){
64614                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64615                 }
64616                 return buf.join('');
64617             }
64618             return t.compiled.call(this, vs, parent);
64619         } catch (e) {
64620             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64621             Roo.log(e.toString());
64622             Roo.log(t.compiled);
64623             return '';
64624         }
64625     },
64626
64627     compileTpl : function(tpl)
64628     {
64629         var fm = Roo.util.Format;
64630         var useF = this.disableFormats !== true;
64631         var sep = Roo.isGecko ? "+" : ",";
64632         var undef = function(str) {
64633             Roo.log("Property not found :"  + str);
64634             return '';
64635         };
64636         
64637         var fn = function(m, name, format, args)
64638         {
64639             //Roo.log(arguments);
64640             args = args ? args.replace(/\\'/g,"'") : args;
64641             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64642             if (typeof(format) == 'undefined') {
64643                 format= 'htmlEncode';
64644             }
64645             if (format == 'raw' ) {
64646                 format = false;
64647             }
64648             
64649             if(name.substr(0, 4) == 'xtpl'){
64650                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64651             }
64652             
64653             // build an array of options to determine if value is undefined..
64654             
64655             // basically get 'xxxx.yyyy' then do
64656             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64657             //    (function () { Roo.log("Property not found"); return ''; })() :
64658             //    ......
64659             
64660             var udef_ar = [];
64661             var lookfor = '';
64662             Roo.each(name.split('.'), function(st) {
64663                 lookfor += (lookfor.length ? '.': '') + st;
64664                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64665             });
64666             
64667             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64668             
64669             
64670             if(format && useF){
64671                 
64672                 args = args ? ',' + args : "";
64673                  
64674                 if(format.substr(0, 5) != "this."){
64675                     format = "fm." + format + '(';
64676                 }else{
64677                     format = 'this.call("'+ format.substr(5) + '", ';
64678                     args = ", values";
64679                 }
64680                 
64681                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64682             }
64683              
64684             if (args.length) {
64685                 // called with xxyx.yuu:(test,test)
64686                 // change to ()
64687                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64688             }
64689             // raw.. - :raw modifier..
64690             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64691             
64692         };
64693         var body;
64694         // branched to use + in gecko and [].join() in others
64695         if(Roo.isGecko){
64696             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64697                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64698                     "';};};";
64699         }else{
64700             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64701             body.push(tpl.body.replace(/(\r\n|\n)/g,
64702                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64703             body.push("'].join('');};};");
64704             body = body.join('');
64705         }
64706         
64707         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64708        
64709         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64710         eval(body);
64711         
64712         return this;
64713     },
64714
64715     applyTemplate : function(values){
64716         return this.master.compiled.call(this, values, {});
64717         //var s = this.subs;
64718     },
64719
64720     apply : function(){
64721         return this.applyTemplate.apply(this, arguments);
64722     }
64723
64724  });
64725
64726 Roo.XTemplate.from = function(el){
64727     el = Roo.getDom(el);
64728     return new Roo.XTemplate(el.value || el.innerHTML);
64729 };